docs/src/Roo_bootstrap_Card.js.html
[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 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 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21586             node.removeAttribute(n);
21587             
21588         }
21589         
21590         var cwhite = this.cwhite;
21591         var cblack = this.cblack;
21592             
21593         function cleanStyle(n,v)
21594         {
21595             if (v.match(/expression/)) { //XSS?? should we even bother..
21596                 node.removeAttribute(n);
21597                 return;
21598             }
21599             
21600             var parts = v.split(/;/);
21601             var clean = [];
21602             
21603             Roo.each(parts, function(p) {
21604                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21605                 if (!p.length) {
21606                     return true;
21607                 }
21608                 var l = p.split(':').shift().replace(/\s+/g,'');
21609                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21610                 
21611                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21612 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21613                     //node.removeAttribute(n);
21614                     return true;
21615                 }
21616                 //Roo.log()
21617                 // only allow 'c whitelisted system attributes'
21618                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21619 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21620                     //node.removeAttribute(n);
21621                     return true;
21622                 }
21623                 
21624                 
21625                  
21626                 
21627                 clean.push(p);
21628                 return true;
21629             });
21630             if (clean.length) { 
21631                 node.setAttribute(n, clean.join(';'));
21632             } else {
21633                 node.removeAttribute(n);
21634             }
21635             
21636         }
21637         
21638         
21639         for (var i = node.attributes.length-1; i > -1 ; i--) {
21640             var a = node.attributes[i];
21641             //console.log(a);
21642             
21643             if (a.name.toLowerCase().substr(0,2)=='on')  {
21644                 node.removeAttribute(a.name);
21645                 continue;
21646             }
21647             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21648                 node.removeAttribute(a.name);
21649                 continue;
21650             }
21651             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21652                 cleanAttr(a.name,a.value); // fixme..
21653                 continue;
21654             }
21655             if (a.name == 'style') {
21656                 cleanStyle(a.name,a.value);
21657                 continue;
21658             }
21659             /// clean up MS crap..
21660             // tecnically this should be a list of valid class'es..
21661             
21662             
21663             if (a.name == 'class') {
21664                 if (a.value.match(/^Mso/)) {
21665                     node.removeAttribute('class');
21666                 }
21667                 
21668                 if (a.value.match(/^body$/)) {
21669                     node.removeAttribute('class');
21670                 }
21671                 continue;
21672             }
21673             
21674             // style cleanup!?
21675             // class cleanup?
21676             
21677         }
21678         
21679         
21680         this.cleanUpChildren(node);
21681         
21682         
21683     },
21684     
21685     /**
21686      * Clean up MS wordisms...
21687      */
21688     cleanWord : function(node)
21689     {
21690         if (!node) {
21691             this.cleanWord(this.doc.body);
21692             return;
21693         }
21694         
21695         if(
21696                 node.nodeName == 'SPAN' &&
21697                 !node.hasAttributes() &&
21698                 node.childNodes.length == 1 &&
21699                 node.firstChild.nodeName == "#text"  
21700         ) {
21701             var textNode = node.firstChild;
21702             node.removeChild(textNode);
21703             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21704                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21705             }
21706             node.parentNode.insertBefore(textNode, node);
21707             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21708                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21709             }
21710             node.parentNode.removeChild(node);
21711         }
21712         
21713         if (node.nodeName == "#text") {
21714             // clean up silly Windows -- stuff?
21715             return; 
21716         }
21717         if (node.nodeName == "#comment") {
21718             node.parentNode.removeChild(node);
21719             // clean up silly Windows -- stuff?
21720             return; 
21721         }
21722         
21723         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21724             node.parentNode.removeChild(node);
21725             return;
21726         }
21727         //Roo.log(node.tagName);
21728         // remove - but keep children..
21729         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21730             //Roo.log('-- removed');
21731             while (node.childNodes.length) {
21732                 var cn = node.childNodes[0];
21733                 node.removeChild(cn);
21734                 node.parentNode.insertBefore(cn, node);
21735                 // move node to parent - and clean it..
21736                 this.cleanWord(cn);
21737             }
21738             node.parentNode.removeChild(node);
21739             /// no need to iterate chidlren = it's got none..
21740             //this.iterateChildren(node, this.cleanWord);
21741             return;
21742         }
21743         // clean styles
21744         if (node.className.length) {
21745             
21746             var cn = node.className.split(/\W+/);
21747             var cna = [];
21748             Roo.each(cn, function(cls) {
21749                 if (cls.match(/Mso[a-zA-Z]+/)) {
21750                     return;
21751                 }
21752                 cna.push(cls);
21753             });
21754             node.className = cna.length ? cna.join(' ') : '';
21755             if (!cna.length) {
21756                 node.removeAttribute("class");
21757             }
21758         }
21759         
21760         if (node.hasAttribute("lang")) {
21761             node.removeAttribute("lang");
21762         }
21763         
21764         if (node.hasAttribute("style")) {
21765             
21766             var styles = node.getAttribute("style").split(";");
21767             var nstyle = [];
21768             Roo.each(styles, function(s) {
21769                 if (!s.match(/:/)) {
21770                     return;
21771                 }
21772                 var kv = s.split(":");
21773                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21774                     return;
21775                 }
21776                 // what ever is left... we allow.
21777                 nstyle.push(s);
21778             });
21779             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21780             if (!nstyle.length) {
21781                 node.removeAttribute('style');
21782             }
21783         }
21784         this.iterateChildren(node, this.cleanWord);
21785         
21786         
21787         
21788     },
21789     /**
21790      * iterateChildren of a Node, calling fn each time, using this as the scole..
21791      * @param {DomNode} node node to iterate children of.
21792      * @param {Function} fn method of this class to call on each item.
21793      */
21794     iterateChildren : function(node, fn)
21795     {
21796         if (!node.childNodes.length) {
21797                 return;
21798         }
21799         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21800            fn.call(this, node.childNodes[i])
21801         }
21802     },
21803     
21804     
21805     /**
21806      * cleanTableWidths.
21807      *
21808      * Quite often pasting from word etc.. results in tables with column and widths.
21809      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21810      *
21811      */
21812     cleanTableWidths : function(node)
21813     {
21814          
21815          
21816         if (!node) {
21817             this.cleanTableWidths(this.doc.body);
21818             return;
21819         }
21820         
21821         // ignore list...
21822         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21823             return; 
21824         }
21825         Roo.log(node.tagName);
21826         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21827             this.iterateChildren(node, this.cleanTableWidths);
21828             return;
21829         }
21830         if (node.hasAttribute('width')) {
21831             node.removeAttribute('width');
21832         }
21833         
21834          
21835         if (node.hasAttribute("style")) {
21836             // pretty basic...
21837             
21838             var styles = node.getAttribute("style").split(";");
21839             var nstyle = [];
21840             Roo.each(styles, function(s) {
21841                 if (!s.match(/:/)) {
21842                     return;
21843                 }
21844                 var kv = s.split(":");
21845                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21846                     return;
21847                 }
21848                 // what ever is left... we allow.
21849                 nstyle.push(s);
21850             });
21851             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21852             if (!nstyle.length) {
21853                 node.removeAttribute('style');
21854             }
21855         }
21856         
21857         this.iterateChildren(node, this.cleanTableWidths);
21858         
21859         
21860     },
21861     
21862     
21863     
21864     
21865     domToHTML : function(currentElement, depth, nopadtext) {
21866         
21867         depth = depth || 0;
21868         nopadtext = nopadtext || false;
21869     
21870         if (!currentElement) {
21871             return this.domToHTML(this.doc.body);
21872         }
21873         
21874         //Roo.log(currentElement);
21875         var j;
21876         var allText = false;
21877         var nodeName = currentElement.nodeName;
21878         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21879         
21880         if  (nodeName == '#text') {
21881             
21882             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21883         }
21884         
21885         
21886         var ret = '';
21887         if (nodeName != 'BODY') {
21888              
21889             var i = 0;
21890             // Prints the node tagName, such as <A>, <IMG>, etc
21891             if (tagName) {
21892                 var attr = [];
21893                 for(i = 0; i < currentElement.attributes.length;i++) {
21894                     // quoting?
21895                     var aname = currentElement.attributes.item(i).name;
21896                     if (!currentElement.attributes.item(i).value.length) {
21897                         continue;
21898                     }
21899                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21900                 }
21901                 
21902                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21903             } 
21904             else {
21905                 
21906                 // eack
21907             }
21908         } else {
21909             tagName = false;
21910         }
21911         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21912             return ret;
21913         }
21914         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21915             nopadtext = true;
21916         }
21917         
21918         
21919         // Traverse the tree
21920         i = 0;
21921         var currentElementChild = currentElement.childNodes.item(i);
21922         var allText = true;
21923         var innerHTML  = '';
21924         lastnode = '';
21925         while (currentElementChild) {
21926             // Formatting code (indent the tree so it looks nice on the screen)
21927             var nopad = nopadtext;
21928             if (lastnode == 'SPAN') {
21929                 nopad  = true;
21930             }
21931             // text
21932             if  (currentElementChild.nodeName == '#text') {
21933                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21934                 toadd = nopadtext ? toadd : toadd.trim();
21935                 if (!nopad && toadd.length > 80) {
21936                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21937                 }
21938                 innerHTML  += toadd;
21939                 
21940                 i++;
21941                 currentElementChild = currentElement.childNodes.item(i);
21942                 lastNode = '';
21943                 continue;
21944             }
21945             allText = false;
21946             
21947             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21948                 
21949             // Recursively traverse the tree structure of the child node
21950             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21951             lastnode = currentElementChild.nodeName;
21952             i++;
21953             currentElementChild=currentElement.childNodes.item(i);
21954         }
21955         
21956         ret += innerHTML;
21957         
21958         if (!allText) {
21959                 // The remaining code is mostly for formatting the tree
21960             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21961         }
21962         
21963         
21964         if (tagName) {
21965             ret+= "</"+tagName+">";
21966         }
21967         return ret;
21968         
21969     },
21970         
21971     applyBlacklists : function()
21972     {
21973         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21974         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21975         
21976         this.white = [];
21977         this.black = [];
21978         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21979             if (b.indexOf(tag) > -1) {
21980                 return;
21981             }
21982             this.white.push(tag);
21983             
21984         }, this);
21985         
21986         Roo.each(w, function(tag) {
21987             if (b.indexOf(tag) > -1) {
21988                 return;
21989             }
21990             if (this.white.indexOf(tag) > -1) {
21991                 return;
21992             }
21993             this.white.push(tag);
21994             
21995         }, this);
21996         
21997         
21998         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21999             if (w.indexOf(tag) > -1) {
22000                 return;
22001             }
22002             this.black.push(tag);
22003             
22004         }, this);
22005         
22006         Roo.each(b, function(tag) {
22007             if (w.indexOf(tag) > -1) {
22008                 return;
22009             }
22010             if (this.black.indexOf(tag) > -1) {
22011                 return;
22012             }
22013             this.black.push(tag);
22014             
22015         }, this);
22016         
22017         
22018         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22019         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22020         
22021         this.cwhite = [];
22022         this.cblack = [];
22023         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22024             if (b.indexOf(tag) > -1) {
22025                 return;
22026             }
22027             this.cwhite.push(tag);
22028             
22029         }, this);
22030         
22031         Roo.each(w, function(tag) {
22032             if (b.indexOf(tag) > -1) {
22033                 return;
22034             }
22035             if (this.cwhite.indexOf(tag) > -1) {
22036                 return;
22037             }
22038             this.cwhite.push(tag);
22039             
22040         }, this);
22041         
22042         
22043         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22044             if (w.indexOf(tag) > -1) {
22045                 return;
22046             }
22047             this.cblack.push(tag);
22048             
22049         }, this);
22050         
22051         Roo.each(b, function(tag) {
22052             if (w.indexOf(tag) > -1) {
22053                 return;
22054             }
22055             if (this.cblack.indexOf(tag) > -1) {
22056                 return;
22057             }
22058             this.cblack.push(tag);
22059             
22060         }, this);
22061     },
22062     
22063     setStylesheets : function(stylesheets)
22064     {
22065         if(typeof(stylesheets) == 'string'){
22066             Roo.get(this.iframe.contentDocument.head).createChild({
22067                 tag : 'link',
22068                 rel : 'stylesheet',
22069                 type : 'text/css',
22070                 href : stylesheets
22071             });
22072             
22073             return;
22074         }
22075         var _this = this;
22076      
22077         Roo.each(stylesheets, function(s) {
22078             if(!s.length){
22079                 return;
22080             }
22081             
22082             Roo.get(_this.iframe.contentDocument.head).createChild({
22083                 tag : 'link',
22084                 rel : 'stylesheet',
22085                 type : 'text/css',
22086                 href : s
22087             });
22088         });
22089
22090         
22091     },
22092     
22093     removeStylesheets : function()
22094     {
22095         var _this = this;
22096         
22097         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22098             s.remove();
22099         });
22100     },
22101     
22102     setStyle : function(style)
22103     {
22104         Roo.get(this.iframe.contentDocument.head).createChild({
22105             tag : 'style',
22106             type : 'text/css',
22107             html : style
22108         });
22109
22110         return;
22111     }
22112     
22113     // hide stuff that is not compatible
22114     /**
22115      * @event blur
22116      * @hide
22117      */
22118     /**
22119      * @event change
22120      * @hide
22121      */
22122     /**
22123      * @event focus
22124      * @hide
22125      */
22126     /**
22127      * @event specialkey
22128      * @hide
22129      */
22130     /**
22131      * @cfg {String} fieldClass @hide
22132      */
22133     /**
22134      * @cfg {String} focusClass @hide
22135      */
22136     /**
22137      * @cfg {String} autoCreate @hide
22138      */
22139     /**
22140      * @cfg {String} inputType @hide
22141      */
22142     /**
22143      * @cfg {String} invalidClass @hide
22144      */
22145     /**
22146      * @cfg {String} invalidText @hide
22147      */
22148     /**
22149      * @cfg {String} msgFx @hide
22150      */
22151     /**
22152      * @cfg {String} validateOnBlur @hide
22153      */
22154 });
22155
22156 Roo.HtmlEditorCore.white = [
22157         'area', 'br', 'img', 'input', 'hr', 'wbr',
22158         
22159        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22160        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22161        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22162        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22163        'table',   'ul',         'xmp', 
22164        
22165        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22166       'thead',   'tr', 
22167      
22168       'dir', 'menu', 'ol', 'ul', 'dl',
22169        
22170       'embed',  'object'
22171 ];
22172
22173
22174 Roo.HtmlEditorCore.black = [
22175     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22176         'applet', // 
22177         'base',   'basefont', 'bgsound', 'blink',  'body', 
22178         'frame',  'frameset', 'head',    'html',   'ilayer', 
22179         'iframe', 'layer',  'link',     'meta',    'object',   
22180         'script', 'style' ,'title',  'xml' // clean later..
22181 ];
22182 Roo.HtmlEditorCore.clean = [
22183     'script', 'style', 'title', 'xml'
22184 ];
22185 Roo.HtmlEditorCore.remove = [
22186     'font'
22187 ];
22188 // attributes..
22189
22190 Roo.HtmlEditorCore.ablack = [
22191     'on'
22192 ];
22193     
22194 Roo.HtmlEditorCore.aclean = [ 
22195     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22196 ];
22197
22198 // protocols..
22199 Roo.HtmlEditorCore.pwhite= [
22200         'http',  'https',  'mailto'
22201 ];
22202
22203 // white listed style attributes.
22204 Roo.HtmlEditorCore.cwhite= [
22205       //  'text-align', /// default is to allow most things..
22206       
22207          
22208 //        'font-size'//??
22209 ];
22210
22211 // black listed style attributes.
22212 Roo.HtmlEditorCore.cblack= [
22213       //  'font-size' -- this can be set by the project 
22214 ];
22215
22216
22217 Roo.HtmlEditorCore.swapCodes   =[ 
22218     [    8211, "--" ], 
22219     [    8212, "--" ], 
22220     [    8216,  "'" ],  
22221     [    8217, "'" ],  
22222     [    8220, '"' ],  
22223     [    8221, '"' ],  
22224     [    8226, "*" ],  
22225     [    8230, "..." ]
22226 ]; 
22227
22228     //<script type="text/javascript">
22229
22230 /*
22231  * Ext JS Library 1.1.1
22232  * Copyright(c) 2006-2007, Ext JS, LLC.
22233  * Licence LGPL
22234  * 
22235  */
22236  
22237  
22238 Roo.form.HtmlEditor = function(config){
22239     
22240     
22241     
22242     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22243     
22244     if (!this.toolbars) {
22245         this.toolbars = [];
22246     }
22247     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22248     
22249     
22250 };
22251
22252 /**
22253  * @class Roo.form.HtmlEditor
22254  * @extends Roo.form.Field
22255  * Provides a lightweight HTML Editor component.
22256  *
22257  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22258  * 
22259  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22260  * supported by this editor.</b><br/><br/>
22261  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22262  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22263  */
22264 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22265     /**
22266      * @cfg {Boolean} clearUp
22267      */
22268     clearUp : true,
22269       /**
22270      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22271      */
22272     toolbars : false,
22273    
22274      /**
22275      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22276      *                        Roo.resizable.
22277      */
22278     resizable : false,
22279      /**
22280      * @cfg {Number} height (in pixels)
22281      */   
22282     height: 300,
22283    /**
22284      * @cfg {Number} width (in pixels)
22285      */   
22286     width: 500,
22287     
22288     /**
22289      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22290      * 
22291      */
22292     stylesheets: false,
22293     
22294     
22295      /**
22296      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22297      * 
22298      */
22299     cblack: false,
22300     /**
22301      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22302      * 
22303      */
22304     cwhite: false,
22305     
22306      /**
22307      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22308      * 
22309      */
22310     black: false,
22311     /**
22312      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22313      * 
22314      */
22315     white: false,
22316     
22317     // id of frame..
22318     frameId: false,
22319     
22320     // private properties
22321     validationEvent : false,
22322     deferHeight: true,
22323     initialized : false,
22324     activated : false,
22325     
22326     onFocus : Roo.emptyFn,
22327     iframePad:3,
22328     hideMode:'offsets',
22329     
22330     actionMode : 'container', // defaults to hiding it...
22331     
22332     defaultAutoCreate : { // modified by initCompnoent..
22333         tag: "textarea",
22334         style:"width:500px;height:300px;",
22335         autocomplete: "new-password"
22336     },
22337
22338     // private
22339     initComponent : function(){
22340         this.addEvents({
22341             /**
22342              * @event initialize
22343              * Fires when the editor is fully initialized (including the iframe)
22344              * @param {HtmlEditor} this
22345              */
22346             initialize: true,
22347             /**
22348              * @event activate
22349              * Fires when the editor is first receives the focus. Any insertion must wait
22350              * until after this event.
22351              * @param {HtmlEditor} this
22352              */
22353             activate: true,
22354              /**
22355              * @event beforesync
22356              * Fires before the textarea is updated with content from the editor iframe. Return false
22357              * to cancel the sync.
22358              * @param {HtmlEditor} this
22359              * @param {String} html
22360              */
22361             beforesync: true,
22362              /**
22363              * @event beforepush
22364              * Fires before the iframe editor is updated with content from the textarea. Return false
22365              * to cancel the push.
22366              * @param {HtmlEditor} this
22367              * @param {String} html
22368              */
22369             beforepush: true,
22370              /**
22371              * @event sync
22372              * Fires when the textarea is updated with content from the editor iframe.
22373              * @param {HtmlEditor} this
22374              * @param {String} html
22375              */
22376             sync: true,
22377              /**
22378              * @event push
22379              * Fires when the iframe editor is updated with content from the textarea.
22380              * @param {HtmlEditor} this
22381              * @param {String} html
22382              */
22383             push: true,
22384              /**
22385              * @event editmodechange
22386              * Fires when the editor switches edit modes
22387              * @param {HtmlEditor} this
22388              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22389              */
22390             editmodechange: true,
22391             /**
22392              * @event editorevent
22393              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22394              * @param {HtmlEditor} this
22395              */
22396             editorevent: true,
22397             /**
22398              * @event firstfocus
22399              * Fires when on first focus - needed by toolbars..
22400              * @param {HtmlEditor} this
22401              */
22402             firstfocus: true,
22403             /**
22404              * @event autosave
22405              * Auto save the htmlEditor value as a file into Events
22406              * @param {HtmlEditor} this
22407              */
22408             autosave: true,
22409             /**
22410              * @event savedpreview
22411              * preview the saved version of htmlEditor
22412              * @param {HtmlEditor} this
22413              */
22414             savedpreview: true,
22415             
22416             /**
22417             * @event stylesheetsclick
22418             * Fires when press the Sytlesheets button
22419             * @param {Roo.HtmlEditorCore} this
22420             */
22421             stylesheetsclick: true
22422         });
22423         this.defaultAutoCreate =  {
22424             tag: "textarea",
22425             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22426             autocomplete: "new-password"
22427         };
22428     },
22429
22430     /**
22431      * Protected method that will not generally be called directly. It
22432      * is called when the editor creates its toolbar. Override this method if you need to
22433      * add custom toolbar buttons.
22434      * @param {HtmlEditor} editor
22435      */
22436     createToolbar : function(editor){
22437         Roo.log("create toolbars");
22438         if (!editor.toolbars || !editor.toolbars.length) {
22439             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22440         }
22441         
22442         for (var i =0 ; i < editor.toolbars.length;i++) {
22443             editor.toolbars[i] = Roo.factory(
22444                     typeof(editor.toolbars[i]) == 'string' ?
22445                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22446                 Roo.form.HtmlEditor);
22447             editor.toolbars[i].init(editor);
22448         }
22449          
22450         
22451     },
22452
22453      
22454     // private
22455     onRender : function(ct, position)
22456     {
22457         var _t = this;
22458         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22459         
22460         this.wrap = this.el.wrap({
22461             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22462         });
22463         
22464         this.editorcore.onRender(ct, position);
22465          
22466         if (this.resizable) {
22467             this.resizeEl = new Roo.Resizable(this.wrap, {
22468                 pinned : true,
22469                 wrap: true,
22470                 dynamic : true,
22471                 minHeight : this.height,
22472                 height: this.height,
22473                 handles : this.resizable,
22474                 width: this.width,
22475                 listeners : {
22476                     resize : function(r, w, h) {
22477                         _t.onResize(w,h); // -something
22478                     }
22479                 }
22480             });
22481             
22482         }
22483         this.createToolbar(this);
22484        
22485         
22486         if(!this.width){
22487             this.setSize(this.wrap.getSize());
22488         }
22489         if (this.resizeEl) {
22490             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22491             // should trigger onReize..
22492         }
22493         
22494         this.keyNav = new Roo.KeyNav(this.el, {
22495             
22496             "tab" : function(e){
22497                 e.preventDefault();
22498                 
22499                 var value = this.getValue();
22500                 
22501                 var start = this.el.dom.selectionStart;
22502                 var end = this.el.dom.selectionEnd;
22503                 
22504                 if(!e.shiftKey){
22505                     
22506                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22507                     this.el.dom.setSelectionRange(end + 1, end + 1);
22508                     return;
22509                 }
22510                 
22511                 var f = value.substring(0, start).split("\t");
22512                 
22513                 if(f.pop().length != 0){
22514                     return;
22515                 }
22516                 
22517                 this.setValue(f.join("\t") + value.substring(end));
22518                 this.el.dom.setSelectionRange(start - 1, start - 1);
22519                 
22520             },
22521             
22522             "home" : function(e){
22523                 e.preventDefault();
22524                 
22525                 var curr = this.el.dom.selectionStart;
22526                 var lines = this.getValue().split("\n");
22527                 
22528                 if(!lines.length){
22529                     return;
22530                 }
22531                 
22532                 if(e.ctrlKey){
22533                     this.el.dom.setSelectionRange(0, 0);
22534                     return;
22535                 }
22536                 
22537                 var pos = 0;
22538                 
22539                 for (var i = 0; i < lines.length;i++) {
22540                     pos += lines[i].length;
22541                     
22542                     if(i != 0){
22543                         pos += 1;
22544                     }
22545                     
22546                     if(pos < curr){
22547                         continue;
22548                     }
22549                     
22550                     pos -= lines[i].length;
22551                     
22552                     break;
22553                 }
22554                 
22555                 if(!e.shiftKey){
22556                     this.el.dom.setSelectionRange(pos, pos);
22557                     return;
22558                 }
22559                 
22560                 this.el.dom.selectionStart = pos;
22561                 this.el.dom.selectionEnd = curr;
22562             },
22563             
22564             "end" : function(e){
22565                 e.preventDefault();
22566                 
22567                 var curr = this.el.dom.selectionStart;
22568                 var lines = this.getValue().split("\n");
22569                 
22570                 if(!lines.length){
22571                     return;
22572                 }
22573                 
22574                 if(e.ctrlKey){
22575                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22576                     return;
22577                 }
22578                 
22579                 var pos = 0;
22580                 
22581                 for (var i = 0; i < lines.length;i++) {
22582                     
22583                     pos += lines[i].length;
22584                     
22585                     if(i != 0){
22586                         pos += 1;
22587                     }
22588                     
22589                     if(pos < curr){
22590                         continue;
22591                     }
22592                     
22593                     break;
22594                 }
22595                 
22596                 if(!e.shiftKey){
22597                     this.el.dom.setSelectionRange(pos, pos);
22598                     return;
22599                 }
22600                 
22601                 this.el.dom.selectionStart = curr;
22602                 this.el.dom.selectionEnd = pos;
22603             },
22604
22605             scope : this,
22606
22607             doRelay : function(foo, bar, hname){
22608                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22609             },
22610
22611             forceKeyDown: true
22612         });
22613         
22614 //        if(this.autosave && this.w){
22615 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22616 //        }
22617     },
22618
22619     // private
22620     onResize : function(w, h)
22621     {
22622         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22623         var ew = false;
22624         var eh = false;
22625         
22626         if(this.el ){
22627             if(typeof w == 'number'){
22628                 var aw = w - this.wrap.getFrameWidth('lr');
22629                 this.el.setWidth(this.adjustWidth('textarea', aw));
22630                 ew = aw;
22631             }
22632             if(typeof h == 'number'){
22633                 var tbh = 0;
22634                 for (var i =0; i < this.toolbars.length;i++) {
22635                     // fixme - ask toolbars for heights?
22636                     tbh += this.toolbars[i].tb.el.getHeight();
22637                     if (this.toolbars[i].footer) {
22638                         tbh += this.toolbars[i].footer.el.getHeight();
22639                     }
22640                 }
22641                 
22642                 
22643                 
22644                 
22645                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22646                 ah -= 5; // knock a few pixes off for look..
22647 //                Roo.log(ah);
22648                 this.el.setHeight(this.adjustWidth('textarea', ah));
22649                 var eh = ah;
22650             }
22651         }
22652         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22653         this.editorcore.onResize(ew,eh);
22654         
22655     },
22656
22657     /**
22658      * Toggles the editor between standard and source edit mode.
22659      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22660      */
22661     toggleSourceEdit : function(sourceEditMode)
22662     {
22663         this.editorcore.toggleSourceEdit(sourceEditMode);
22664         
22665         if(this.editorcore.sourceEditMode){
22666             Roo.log('editor - showing textarea');
22667             
22668 //            Roo.log('in');
22669 //            Roo.log(this.syncValue());
22670             this.editorcore.syncValue();
22671             this.el.removeClass('x-hidden');
22672             this.el.dom.removeAttribute('tabIndex');
22673             this.el.focus();
22674             
22675             for (var i = 0; i < this.toolbars.length; i++) {
22676                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22677                     this.toolbars[i].tb.hide();
22678                     this.toolbars[i].footer.hide();
22679                 }
22680             }
22681             
22682         }else{
22683             Roo.log('editor - hiding textarea');
22684 //            Roo.log('out')
22685 //            Roo.log(this.pushValue()); 
22686             this.editorcore.pushValue();
22687             
22688             this.el.addClass('x-hidden');
22689             this.el.dom.setAttribute('tabIndex', -1);
22690             
22691             for (var i = 0; i < this.toolbars.length; i++) {
22692                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22693                     this.toolbars[i].tb.show();
22694                     this.toolbars[i].footer.show();
22695                 }
22696             }
22697             
22698             //this.deferFocus();
22699         }
22700         
22701         this.setSize(this.wrap.getSize());
22702         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22703         
22704         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22705     },
22706  
22707     // private (for BoxComponent)
22708     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22709
22710     // private (for BoxComponent)
22711     getResizeEl : function(){
22712         return this.wrap;
22713     },
22714
22715     // private (for BoxComponent)
22716     getPositionEl : function(){
22717         return this.wrap;
22718     },
22719
22720     // private
22721     initEvents : function(){
22722         this.originalValue = this.getValue();
22723     },
22724
22725     /**
22726      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22727      * @method
22728      */
22729     markInvalid : Roo.emptyFn,
22730     /**
22731      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22732      * @method
22733      */
22734     clearInvalid : Roo.emptyFn,
22735
22736     setValue : function(v){
22737         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22738         this.editorcore.pushValue();
22739     },
22740
22741      
22742     // private
22743     deferFocus : function(){
22744         this.focus.defer(10, this);
22745     },
22746
22747     // doc'ed in Field
22748     focus : function(){
22749         this.editorcore.focus();
22750         
22751     },
22752       
22753
22754     // private
22755     onDestroy : function(){
22756         
22757         
22758         
22759         if(this.rendered){
22760             
22761             for (var i =0; i < this.toolbars.length;i++) {
22762                 // fixme - ask toolbars for heights?
22763                 this.toolbars[i].onDestroy();
22764             }
22765             
22766             this.wrap.dom.innerHTML = '';
22767             this.wrap.remove();
22768         }
22769     },
22770
22771     // private
22772     onFirstFocus : function(){
22773         //Roo.log("onFirstFocus");
22774         this.editorcore.onFirstFocus();
22775          for (var i =0; i < this.toolbars.length;i++) {
22776             this.toolbars[i].onFirstFocus();
22777         }
22778         
22779     },
22780     
22781     // private
22782     syncValue : function()
22783     {
22784         this.editorcore.syncValue();
22785     },
22786     
22787     pushValue : function()
22788     {
22789         this.editorcore.pushValue();
22790     },
22791     
22792     setStylesheets : function(stylesheets)
22793     {
22794         this.editorcore.setStylesheets(stylesheets);
22795     },
22796     
22797     removeStylesheets : function()
22798     {
22799         this.editorcore.removeStylesheets();
22800     }
22801      
22802     
22803     // hide stuff that is not compatible
22804     /**
22805      * @event blur
22806      * @hide
22807      */
22808     /**
22809      * @event change
22810      * @hide
22811      */
22812     /**
22813      * @event focus
22814      * @hide
22815      */
22816     /**
22817      * @event specialkey
22818      * @hide
22819      */
22820     /**
22821      * @cfg {String} fieldClass @hide
22822      */
22823     /**
22824      * @cfg {String} focusClass @hide
22825      */
22826     /**
22827      * @cfg {String} autoCreate @hide
22828      */
22829     /**
22830      * @cfg {String} inputType @hide
22831      */
22832     /**
22833      * @cfg {String} invalidClass @hide
22834      */
22835     /**
22836      * @cfg {String} invalidText @hide
22837      */
22838     /**
22839      * @cfg {String} msgFx @hide
22840      */
22841     /**
22842      * @cfg {String} validateOnBlur @hide
22843      */
22844 });
22845  
22846     // <script type="text/javascript">
22847 /*
22848  * Based on
22849  * Ext JS Library 1.1.1
22850  * Copyright(c) 2006-2007, Ext JS, LLC.
22851  *  
22852  
22853  */
22854
22855 /**
22856  * @class Roo.form.HtmlEditorToolbar1
22857  * Basic Toolbar
22858  * 
22859  * Usage:
22860  *
22861  new Roo.form.HtmlEditor({
22862     ....
22863     toolbars : [
22864         new Roo.form.HtmlEditorToolbar1({
22865             disable : { fonts: 1 , format: 1, ..., ... , ...],
22866             btns : [ .... ]
22867         })
22868     }
22869      
22870  * 
22871  * @cfg {Object} disable List of elements to disable..
22872  * @cfg {Array} btns List of additional buttons.
22873  * 
22874  * 
22875  * NEEDS Extra CSS? 
22876  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22877  */
22878  
22879 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22880 {
22881     
22882     Roo.apply(this, config);
22883     
22884     // default disabled, based on 'good practice'..
22885     this.disable = this.disable || {};
22886     Roo.applyIf(this.disable, {
22887         fontSize : true,
22888         colors : true,
22889         specialElements : true
22890     });
22891     
22892     
22893     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22894     // dont call parent... till later.
22895 }
22896
22897 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22898     
22899     tb: false,
22900     
22901     rendered: false,
22902     
22903     editor : false,
22904     editorcore : false,
22905     /**
22906      * @cfg {Object} disable  List of toolbar elements to disable
22907          
22908      */
22909     disable : false,
22910     
22911     
22912      /**
22913      * @cfg {String} createLinkText The default text for the create link prompt
22914      */
22915     createLinkText : 'Please enter the URL for the link:',
22916     /**
22917      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22918      */
22919     defaultLinkValue : 'http:/'+'/',
22920    
22921     
22922       /**
22923      * @cfg {Array} fontFamilies An array of available font families
22924      */
22925     fontFamilies : [
22926         'Arial',
22927         'Courier New',
22928         'Tahoma',
22929         'Times New Roman',
22930         'Verdana'
22931     ],
22932     
22933     specialChars : [
22934            "&#169;",
22935           "&#174;",     
22936           "&#8482;",    
22937           "&#163;" ,    
22938          // "&#8212;",    
22939           "&#8230;",    
22940           "&#247;" ,    
22941         //  "&#225;" ,     ?? a acute?
22942            "&#8364;"    , //Euro
22943        //   "&#8220;"    ,
22944         //  "&#8221;"    ,
22945         //  "&#8226;"    ,
22946           "&#176;"  //   , // degrees
22947
22948          // "&#233;"     , // e ecute
22949          // "&#250;"     , // u ecute?
22950     ],
22951     
22952     specialElements : [
22953         {
22954             text: "Insert Table",
22955             xtype: 'MenuItem',
22956             xns : Roo.Menu,
22957             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
22958                 
22959         },
22960         {    
22961             text: "Insert Image",
22962             xtype: 'MenuItem',
22963             xns : Roo.Menu,
22964             ihtml : '<img src="about:blank"/>'
22965             
22966         }
22967         
22968          
22969     ],
22970     
22971     
22972     inputElements : [ 
22973             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
22974             "input:submit", "input:button", "select", "textarea", "label" ],
22975     formats : [
22976         ["p"] ,  
22977         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
22978         ["pre"],[ "code"], 
22979         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22980         ['div'],['span'],
22981         ['sup'],['sub']
22982     ],
22983     
22984     cleanStyles : [
22985         "font-size"
22986     ],
22987      /**
22988      * @cfg {String} defaultFont default font to use.
22989      */
22990     defaultFont: 'tahoma',
22991    
22992     fontSelect : false,
22993     
22994     
22995     formatCombo : false,
22996     
22997     init : function(editor)
22998     {
22999         this.editor = editor;
23000         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23001         var editorcore = this.editorcore;
23002         
23003         var _t = this;
23004         
23005         var fid = editorcore.frameId;
23006         var etb = this;
23007         function btn(id, toggle, handler){
23008             var xid = fid + '-'+ id ;
23009             return {
23010                 id : xid,
23011                 cmd : id,
23012                 cls : 'x-btn-icon x-edit-'+id,
23013                 enableToggle:toggle !== false,
23014                 scope: _t, // was editor...
23015                 handler:handler||_t.relayBtnCmd,
23016                 clickEvent:'mousedown',
23017                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23018                 tabIndex:-1
23019             };
23020         }
23021         
23022         
23023         
23024         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23025         this.tb = tb;
23026          // stop form submits
23027         tb.el.on('click', function(e){
23028             e.preventDefault(); // what does this do?
23029         });
23030
23031         if(!this.disable.font) { // && !Roo.isSafari){
23032             /* why no safari for fonts 
23033             editor.fontSelect = tb.el.createChild({
23034                 tag:'select',
23035                 tabIndex: -1,
23036                 cls:'x-font-select',
23037                 html: this.createFontOptions()
23038             });
23039             
23040             editor.fontSelect.on('change', function(){
23041                 var font = editor.fontSelect.dom.value;
23042                 editor.relayCmd('fontname', font);
23043                 editor.deferFocus();
23044             }, editor);
23045             
23046             tb.add(
23047                 editor.fontSelect.dom,
23048                 '-'
23049             );
23050             */
23051             
23052         };
23053         if(!this.disable.formats){
23054             this.formatCombo = new Roo.form.ComboBox({
23055                 store: new Roo.data.SimpleStore({
23056                     id : 'tag',
23057                     fields: ['tag'],
23058                     data : this.formats // from states.js
23059                 }),
23060                 blockFocus : true,
23061                 name : '',
23062                 //autoCreate : {tag: "div",  size: "20"},
23063                 displayField:'tag',
23064                 typeAhead: false,
23065                 mode: 'local',
23066                 editable : false,
23067                 triggerAction: 'all',
23068                 emptyText:'Add tag',
23069                 selectOnFocus:true,
23070                 width:135,
23071                 listeners : {
23072                     'select': function(c, r, i) {
23073                         editorcore.insertTag(r.get('tag'));
23074                         editor.focus();
23075                     }
23076                 }
23077
23078             });
23079             tb.addField(this.formatCombo);
23080             
23081         }
23082         
23083         if(!this.disable.format){
23084             tb.add(
23085                 btn('bold'),
23086                 btn('italic'),
23087                 btn('underline'),
23088                 btn('strikethrough')
23089             );
23090         };
23091         if(!this.disable.fontSize){
23092             tb.add(
23093                 '-',
23094                 
23095                 
23096                 btn('increasefontsize', false, editorcore.adjustFont),
23097                 btn('decreasefontsize', false, editorcore.adjustFont)
23098             );
23099         };
23100         
23101         
23102         if(!this.disable.colors){
23103             tb.add(
23104                 '-', {
23105                     id:editorcore.frameId +'-forecolor',
23106                     cls:'x-btn-icon x-edit-forecolor',
23107                     clickEvent:'mousedown',
23108                     tooltip: this.buttonTips['forecolor'] || undefined,
23109                     tabIndex:-1,
23110                     menu : new Roo.menu.ColorMenu({
23111                         allowReselect: true,
23112                         focus: Roo.emptyFn,
23113                         value:'000000',
23114                         plain:true,
23115                         selectHandler: function(cp, color){
23116                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23117                             editor.deferFocus();
23118                         },
23119                         scope: editorcore,
23120                         clickEvent:'mousedown'
23121                     })
23122                 }, {
23123                     id:editorcore.frameId +'backcolor',
23124                     cls:'x-btn-icon x-edit-backcolor',
23125                     clickEvent:'mousedown',
23126                     tooltip: this.buttonTips['backcolor'] || undefined,
23127                     tabIndex:-1,
23128                     menu : new Roo.menu.ColorMenu({
23129                         focus: Roo.emptyFn,
23130                         value:'FFFFFF',
23131                         plain:true,
23132                         allowReselect: true,
23133                         selectHandler: function(cp, color){
23134                             if(Roo.isGecko){
23135                                 editorcore.execCmd('useCSS', false);
23136                                 editorcore.execCmd('hilitecolor', color);
23137                                 editorcore.execCmd('useCSS', true);
23138                                 editor.deferFocus();
23139                             }else{
23140                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23141                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23142                                 editor.deferFocus();
23143                             }
23144                         },
23145                         scope:editorcore,
23146                         clickEvent:'mousedown'
23147                     })
23148                 }
23149             );
23150         };
23151         // now add all the items...
23152         
23153
23154         if(!this.disable.alignments){
23155             tb.add(
23156                 '-',
23157                 btn('justifyleft'),
23158                 btn('justifycenter'),
23159                 btn('justifyright')
23160             );
23161         };
23162
23163         //if(!Roo.isSafari){
23164             if(!this.disable.links){
23165                 tb.add(
23166                     '-',
23167                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23168                 );
23169             };
23170
23171             if(!this.disable.lists){
23172                 tb.add(
23173                     '-',
23174                     btn('insertorderedlist'),
23175                     btn('insertunorderedlist')
23176                 );
23177             }
23178             if(!this.disable.sourceEdit){
23179                 tb.add(
23180                     '-',
23181                     btn('sourceedit', true, function(btn){
23182                         this.toggleSourceEdit(btn.pressed);
23183                     })
23184                 );
23185             }
23186         //}
23187         
23188         var smenu = { };
23189         // special menu.. - needs to be tidied up..
23190         if (!this.disable.special) {
23191             smenu = {
23192                 text: "&#169;",
23193                 cls: 'x-edit-none',
23194                 
23195                 menu : {
23196                     items : []
23197                 }
23198             };
23199             for (var i =0; i < this.specialChars.length; i++) {
23200                 smenu.menu.items.push({
23201                     
23202                     html: this.specialChars[i],
23203                     handler: function(a,b) {
23204                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23205                         //editor.insertAtCursor(a.html);
23206                         
23207                     },
23208                     tabIndex:-1
23209                 });
23210             }
23211             
23212             
23213             tb.add(smenu);
23214             
23215             
23216         }
23217         
23218         var cmenu = { };
23219         if (!this.disable.cleanStyles) {
23220             cmenu = {
23221                 cls: 'x-btn-icon x-btn-clear',
23222                 
23223                 menu : {
23224                     items : []
23225                 }
23226             };
23227             for (var i =0; i < this.cleanStyles.length; i++) {
23228                 cmenu.menu.items.push({
23229                     actiontype : this.cleanStyles[i],
23230                     html: 'Remove ' + this.cleanStyles[i],
23231                     handler: function(a,b) {
23232 //                        Roo.log(a);
23233 //                        Roo.log(b);
23234                         var c = Roo.get(editorcore.doc.body);
23235                         c.select('[style]').each(function(s) {
23236                             s.dom.style.removeProperty(a.actiontype);
23237                         });
23238                         editorcore.syncValue();
23239                     },
23240                     tabIndex:-1
23241                 });
23242             }
23243              cmenu.menu.items.push({
23244                 actiontype : 'tablewidths',
23245                 html: 'Remove Table Widths',
23246                 handler: function(a,b) {
23247                     editorcore.cleanTableWidths();
23248                     editorcore.syncValue();
23249                 },
23250                 tabIndex:-1
23251             });
23252             cmenu.menu.items.push({
23253                 actiontype : 'word',
23254                 html: 'Remove MS Word Formating',
23255                 handler: function(a,b) {
23256                     editorcore.cleanWord();
23257                     editorcore.syncValue();
23258                 },
23259                 tabIndex:-1
23260             });
23261             
23262             cmenu.menu.items.push({
23263                 actiontype : 'all',
23264                 html: 'Remove All Styles',
23265                 handler: function(a,b) {
23266                     
23267                     var c = Roo.get(editorcore.doc.body);
23268                     c.select('[style]').each(function(s) {
23269                         s.dom.removeAttribute('style');
23270                     });
23271                     editorcore.syncValue();
23272                 },
23273                 tabIndex:-1
23274             });
23275             
23276             cmenu.menu.items.push({
23277                 actiontype : 'all',
23278                 html: 'Remove All CSS Classes',
23279                 handler: function(a,b) {
23280                     
23281                     var c = Roo.get(editorcore.doc.body);
23282                     c.select('[class]').each(function(s) {
23283                         s.dom.removeAttribute('class');
23284                     });
23285                     editorcore.cleanWord();
23286                     editorcore.syncValue();
23287                 },
23288                 tabIndex:-1
23289             });
23290             
23291              cmenu.menu.items.push({
23292                 actiontype : 'tidy',
23293                 html: 'Tidy HTML Source',
23294                 handler: function(a,b) {
23295                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23296                     editorcore.syncValue();
23297                 },
23298                 tabIndex:-1
23299             });
23300             
23301             
23302             tb.add(cmenu);
23303         }
23304          
23305         if (!this.disable.specialElements) {
23306             var semenu = {
23307                 text: "Other;",
23308                 cls: 'x-edit-none',
23309                 menu : {
23310                     items : []
23311                 }
23312             };
23313             for (var i =0; i < this.specialElements.length; i++) {
23314                 semenu.menu.items.push(
23315                     Roo.apply({ 
23316                         handler: function(a,b) {
23317                             editor.insertAtCursor(this.ihtml);
23318                         }
23319                     }, this.specialElements[i])
23320                 );
23321                     
23322             }
23323             
23324             tb.add(semenu);
23325             
23326             
23327         }
23328          
23329         
23330         if (this.btns) {
23331             for(var i =0; i< this.btns.length;i++) {
23332                 var b = Roo.factory(this.btns[i],Roo.form);
23333                 b.cls =  'x-edit-none';
23334                 
23335                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23336                     b.cls += ' x-init-enable';
23337                 }
23338                 
23339                 b.scope = editorcore;
23340                 tb.add(b);
23341             }
23342         
23343         }
23344         
23345         
23346         
23347         // disable everything...
23348         
23349         this.tb.items.each(function(item){
23350             
23351            if(
23352                 item.id != editorcore.frameId+ '-sourceedit' && 
23353                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23354             ){
23355                 
23356                 item.disable();
23357             }
23358         });
23359         this.rendered = true;
23360         
23361         // the all the btns;
23362         editor.on('editorevent', this.updateToolbar, this);
23363         // other toolbars need to implement this..
23364         //editor.on('editmodechange', this.updateToolbar, this);
23365     },
23366     
23367     
23368     relayBtnCmd : function(btn) {
23369         this.editorcore.relayCmd(btn.cmd);
23370     },
23371     // private used internally
23372     createLink : function(){
23373         Roo.log("create link?");
23374         var url = prompt(this.createLinkText, this.defaultLinkValue);
23375         if(url && url != 'http:/'+'/'){
23376             this.editorcore.relayCmd('createlink', url);
23377         }
23378     },
23379
23380     
23381     /**
23382      * Protected method that will not generally be called directly. It triggers
23383      * a toolbar update by reading the markup state of the current selection in the editor.
23384      */
23385     updateToolbar: function(){
23386
23387         if(!this.editorcore.activated){
23388             this.editor.onFirstFocus();
23389             return;
23390         }
23391
23392         var btns = this.tb.items.map, 
23393             doc = this.editorcore.doc,
23394             frameId = this.editorcore.frameId;
23395
23396         if(!this.disable.font && !Roo.isSafari){
23397             /*
23398             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23399             if(name != this.fontSelect.dom.value){
23400                 this.fontSelect.dom.value = name;
23401             }
23402             */
23403         }
23404         if(!this.disable.format){
23405             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23406             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23407             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23408             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23409         }
23410         if(!this.disable.alignments){
23411             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23412             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23413             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23414         }
23415         if(!Roo.isSafari && !this.disable.lists){
23416             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23417             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23418         }
23419         
23420         var ans = this.editorcore.getAllAncestors();
23421         if (this.formatCombo) {
23422             
23423             
23424             var store = this.formatCombo.store;
23425             this.formatCombo.setValue("");
23426             for (var i =0; i < ans.length;i++) {
23427                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23428                     // select it..
23429                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23430                     break;
23431                 }
23432             }
23433         }
23434         
23435         
23436         
23437         // hides menus... - so this cant be on a menu...
23438         Roo.menu.MenuMgr.hideAll();
23439
23440         //this.editorsyncValue();
23441     },
23442    
23443     
23444     createFontOptions : function(){
23445         var buf = [], fs = this.fontFamilies, ff, lc;
23446         
23447         
23448         
23449         for(var i = 0, len = fs.length; i< len; i++){
23450             ff = fs[i];
23451             lc = ff.toLowerCase();
23452             buf.push(
23453                 '<option value="',lc,'" style="font-family:',ff,';"',
23454                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23455                     ff,
23456                 '</option>'
23457             );
23458         }
23459         return buf.join('');
23460     },
23461     
23462     toggleSourceEdit : function(sourceEditMode){
23463         
23464         Roo.log("toolbar toogle");
23465         if(sourceEditMode === undefined){
23466             sourceEditMode = !this.sourceEditMode;
23467         }
23468         this.sourceEditMode = sourceEditMode === true;
23469         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23470         // just toggle the button?
23471         if(btn.pressed !== this.sourceEditMode){
23472             btn.toggle(this.sourceEditMode);
23473             return;
23474         }
23475         
23476         if(sourceEditMode){
23477             Roo.log("disabling buttons");
23478             this.tb.items.each(function(item){
23479                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23480                     item.disable();
23481                 }
23482             });
23483           
23484         }else{
23485             Roo.log("enabling buttons");
23486             if(this.editorcore.initialized){
23487                 this.tb.items.each(function(item){
23488                     item.enable();
23489                 });
23490             }
23491             
23492         }
23493         Roo.log("calling toggole on editor");
23494         // tell the editor that it's been pressed..
23495         this.editor.toggleSourceEdit(sourceEditMode);
23496        
23497     },
23498      /**
23499      * Object collection of toolbar tooltips for the buttons in the editor. The key
23500      * is the command id associated with that button and the value is a valid QuickTips object.
23501      * For example:
23502 <pre><code>
23503 {
23504     bold : {
23505         title: 'Bold (Ctrl+B)',
23506         text: 'Make the selected text bold.',
23507         cls: 'x-html-editor-tip'
23508     },
23509     italic : {
23510         title: 'Italic (Ctrl+I)',
23511         text: 'Make the selected text italic.',
23512         cls: 'x-html-editor-tip'
23513     },
23514     ...
23515 </code></pre>
23516     * @type Object
23517      */
23518     buttonTips : {
23519         bold : {
23520             title: 'Bold (Ctrl+B)',
23521             text: 'Make the selected text bold.',
23522             cls: 'x-html-editor-tip'
23523         },
23524         italic : {
23525             title: 'Italic (Ctrl+I)',
23526             text: 'Make the selected text italic.',
23527             cls: 'x-html-editor-tip'
23528         },
23529         underline : {
23530             title: 'Underline (Ctrl+U)',
23531             text: 'Underline the selected text.',
23532             cls: 'x-html-editor-tip'
23533         },
23534         strikethrough : {
23535             title: 'Strikethrough',
23536             text: 'Strikethrough the selected text.',
23537             cls: 'x-html-editor-tip'
23538         },
23539         increasefontsize : {
23540             title: 'Grow Text',
23541             text: 'Increase the font size.',
23542             cls: 'x-html-editor-tip'
23543         },
23544         decreasefontsize : {
23545             title: 'Shrink Text',
23546             text: 'Decrease the font size.',
23547             cls: 'x-html-editor-tip'
23548         },
23549         backcolor : {
23550             title: 'Text Highlight Color',
23551             text: 'Change the background color of the selected text.',
23552             cls: 'x-html-editor-tip'
23553         },
23554         forecolor : {
23555             title: 'Font Color',
23556             text: 'Change the color of the selected text.',
23557             cls: 'x-html-editor-tip'
23558         },
23559         justifyleft : {
23560             title: 'Align Text Left',
23561             text: 'Align text to the left.',
23562             cls: 'x-html-editor-tip'
23563         },
23564         justifycenter : {
23565             title: 'Center Text',
23566             text: 'Center text in the editor.',
23567             cls: 'x-html-editor-tip'
23568         },
23569         justifyright : {
23570             title: 'Align Text Right',
23571             text: 'Align text to the right.',
23572             cls: 'x-html-editor-tip'
23573         },
23574         insertunorderedlist : {
23575             title: 'Bullet List',
23576             text: 'Start a bulleted list.',
23577             cls: 'x-html-editor-tip'
23578         },
23579         insertorderedlist : {
23580             title: 'Numbered List',
23581             text: 'Start a numbered list.',
23582             cls: 'x-html-editor-tip'
23583         },
23584         createlink : {
23585             title: 'Hyperlink',
23586             text: 'Make the selected text a hyperlink.',
23587             cls: 'x-html-editor-tip'
23588         },
23589         sourceedit : {
23590             title: 'Source Edit',
23591             text: 'Switch to source editing mode.',
23592             cls: 'x-html-editor-tip'
23593         }
23594     },
23595     // private
23596     onDestroy : function(){
23597         if(this.rendered){
23598             
23599             this.tb.items.each(function(item){
23600                 if(item.menu){
23601                     item.menu.removeAll();
23602                     if(item.menu.el){
23603                         item.menu.el.destroy();
23604                     }
23605                 }
23606                 item.destroy();
23607             });
23608              
23609         }
23610     },
23611     onFirstFocus: function() {
23612         this.tb.items.each(function(item){
23613            item.enable();
23614         });
23615     }
23616 });
23617
23618
23619
23620
23621 // <script type="text/javascript">
23622 /*
23623  * Based on
23624  * Ext JS Library 1.1.1
23625  * Copyright(c) 2006-2007, Ext JS, LLC.
23626  *  
23627  
23628  */
23629
23630  
23631 /**
23632  * @class Roo.form.HtmlEditor.ToolbarContext
23633  * Context Toolbar
23634  * 
23635  * Usage:
23636  *
23637  new Roo.form.HtmlEditor({
23638     ....
23639     toolbars : [
23640         { xtype: 'ToolbarStandard', styles : {} }
23641         { xtype: 'ToolbarContext', disable : {} }
23642     ]
23643 })
23644
23645      
23646  * 
23647  * @config : {Object} disable List of elements to disable.. (not done yet.)
23648  * @config : {Object} styles  Map of styles available.
23649  * 
23650  */
23651
23652 Roo.form.HtmlEditor.ToolbarContext = function(config)
23653 {
23654     
23655     Roo.apply(this, config);
23656     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23657     // dont call parent... till later.
23658     this.styles = this.styles || {};
23659 }
23660
23661  
23662
23663 Roo.form.HtmlEditor.ToolbarContext.types = {
23664     'IMG' : {
23665         width : {
23666             title: "Width",
23667             width: 40
23668         },
23669         height:  {
23670             title: "Height",
23671             width: 40
23672         },
23673         align: {
23674             title: "Align",
23675             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23676             width : 80
23677             
23678         },
23679         border: {
23680             title: "Border",
23681             width: 40
23682         },
23683         alt: {
23684             title: "Alt",
23685             width: 120
23686         },
23687         src : {
23688             title: "Src",
23689             width: 220
23690         }
23691         
23692     },
23693     'A' : {
23694         name : {
23695             title: "Name",
23696             width: 50
23697         },
23698         target:  {
23699             title: "Target",
23700             width: 120
23701         },
23702         href:  {
23703             title: "Href",
23704             width: 220
23705         } // border?
23706         
23707     },
23708     'TABLE' : {
23709         rows : {
23710             title: "Rows",
23711             width: 20
23712         },
23713         cols : {
23714             title: "Cols",
23715             width: 20
23716         },
23717         width : {
23718             title: "Width",
23719             width: 40
23720         },
23721         height : {
23722             title: "Height",
23723             width: 40
23724         },
23725         border : {
23726             title: "Border",
23727             width: 20
23728         }
23729     },
23730     'TD' : {
23731         width : {
23732             title: "Width",
23733             width: 40
23734         },
23735         height : {
23736             title: "Height",
23737             width: 40
23738         },   
23739         align: {
23740             title: "Align",
23741             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23742             width: 80
23743         },
23744         valign: {
23745             title: "Valign",
23746             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23747             width: 80
23748         },
23749         colspan: {
23750             title: "Colspan",
23751             width: 20
23752             
23753         },
23754          'font-family'  : {
23755             title : "Font",
23756             style : 'fontFamily',
23757             displayField: 'display',
23758             optname : 'font-family',
23759             width: 140
23760         }
23761     },
23762     'INPUT' : {
23763         name : {
23764             title: "name",
23765             width: 120
23766         },
23767         value : {
23768             title: "Value",
23769             width: 120
23770         },
23771         width : {
23772             title: "Width",
23773             width: 40
23774         }
23775     },
23776     'LABEL' : {
23777         'for' : {
23778             title: "For",
23779             width: 120
23780         }
23781     },
23782     'TEXTAREA' : {
23783           name : {
23784             title: "name",
23785             width: 120
23786         },
23787         rows : {
23788             title: "Rows",
23789             width: 20
23790         },
23791         cols : {
23792             title: "Cols",
23793             width: 20
23794         }
23795     },
23796     'SELECT' : {
23797         name : {
23798             title: "name",
23799             width: 120
23800         },
23801         selectoptions : {
23802             title: "Options",
23803             width: 200
23804         }
23805     },
23806     
23807     // should we really allow this??
23808     // should this just be 
23809     'BODY' : {
23810         title : {
23811             title: "Title",
23812             width: 200,
23813             disabled : true
23814         }
23815     },
23816     'SPAN' : {
23817         'font-family'  : {
23818             title : "Font",
23819             style : 'fontFamily',
23820             displayField: 'display',
23821             optname : 'font-family',
23822             width: 140
23823         }
23824     },
23825     'DIV' : {
23826         'font-family'  : {
23827             title : "Font",
23828             style : 'fontFamily',
23829             displayField: 'display',
23830             optname : 'font-family',
23831             width: 140
23832         }
23833     },
23834      'P' : {
23835         'font-family'  : {
23836             title : "Font",
23837             style : 'fontFamily',
23838             displayField: 'display',
23839             optname : 'font-family',
23840             width: 140
23841         }
23842     },
23843     
23844     '*' : {
23845         // empty..
23846     }
23847
23848 };
23849
23850 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23851 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23852
23853 Roo.form.HtmlEditor.ToolbarContext.options = {
23854         'font-family'  : [ 
23855                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23856                 [ 'Courier New', 'Courier New'],
23857                 [ 'Tahoma', 'Tahoma'],
23858                 [ 'Times New Roman,serif', 'Times'],
23859                 [ 'Verdana','Verdana' ]
23860         ]
23861 };
23862
23863 // fixme - these need to be configurable..
23864  
23865
23866 //Roo.form.HtmlEditor.ToolbarContext.types
23867
23868
23869 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23870     
23871     tb: false,
23872     
23873     rendered: false,
23874     
23875     editor : false,
23876     editorcore : false,
23877     /**
23878      * @cfg {Object} disable  List of toolbar elements to disable
23879          
23880      */
23881     disable : false,
23882     /**
23883      * @cfg {Object} styles List of styles 
23884      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23885      *
23886      * These must be defined in the page, so they get rendered correctly..
23887      * .headline { }
23888      * TD.underline { }
23889      * 
23890      */
23891     styles : false,
23892     
23893     options: false,
23894     
23895     toolbars : false,
23896     
23897     init : function(editor)
23898     {
23899         this.editor = editor;
23900         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23901         var editorcore = this.editorcore;
23902         
23903         var fid = editorcore.frameId;
23904         var etb = this;
23905         function btn(id, toggle, handler){
23906             var xid = fid + '-'+ id ;
23907             return {
23908                 id : xid,
23909                 cmd : id,
23910                 cls : 'x-btn-icon x-edit-'+id,
23911                 enableToggle:toggle !== false,
23912                 scope: editorcore, // was editor...
23913                 handler:handler||editorcore.relayBtnCmd,
23914                 clickEvent:'mousedown',
23915                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23916                 tabIndex:-1
23917             };
23918         }
23919         // create a new element.
23920         var wdiv = editor.wrap.createChild({
23921                 tag: 'div'
23922             }, editor.wrap.dom.firstChild.nextSibling, true);
23923         
23924         // can we do this more than once??
23925         
23926          // stop form submits
23927       
23928  
23929         // disable everything...
23930         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23931         this.toolbars = {};
23932            
23933         for (var i in  ty) {
23934           
23935             this.toolbars[i] = this.buildToolbar(ty[i],i);
23936         }
23937         this.tb = this.toolbars.BODY;
23938         this.tb.el.show();
23939         this.buildFooter();
23940         this.footer.show();
23941         editor.on('hide', function( ) { this.footer.hide() }, this);
23942         editor.on('show', function( ) { this.footer.show() }, this);
23943         
23944          
23945         this.rendered = true;
23946         
23947         // the all the btns;
23948         editor.on('editorevent', this.updateToolbar, this);
23949         // other toolbars need to implement this..
23950         //editor.on('editmodechange', this.updateToolbar, this);
23951     },
23952     
23953     
23954     
23955     /**
23956      * Protected method that will not generally be called directly. It triggers
23957      * a toolbar update by reading the markup state of the current selection in the editor.
23958      *
23959      * Note you can force an update by calling on('editorevent', scope, false)
23960      */
23961     updateToolbar: function(editor,ev,sel){
23962
23963         //Roo.log(ev);
23964         // capture mouse up - this is handy for selecting images..
23965         // perhaps should go somewhere else...
23966         if(!this.editorcore.activated){
23967              this.editor.onFirstFocus();
23968             return;
23969         }
23970         
23971         
23972         
23973         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23974         // selectNode - might want to handle IE?
23975         if (ev &&
23976             (ev.type == 'mouseup' || ev.type == 'click' ) &&
23977             ev.target && ev.target.tagName == 'IMG') {
23978             // they have click on an image...
23979             // let's see if we can change the selection...
23980             sel = ev.target;
23981          
23982               var nodeRange = sel.ownerDocument.createRange();
23983             try {
23984                 nodeRange.selectNode(sel);
23985             } catch (e) {
23986                 nodeRange.selectNodeContents(sel);
23987             }
23988             //nodeRange.collapse(true);
23989             var s = this.editorcore.win.getSelection();
23990             s.removeAllRanges();
23991             s.addRange(nodeRange);
23992         }  
23993         
23994       
23995         var updateFooter = sel ? false : true;
23996         
23997         
23998         var ans = this.editorcore.getAllAncestors();
23999         
24000         // pick
24001         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24002         
24003         if (!sel) { 
24004             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
24005             sel = sel ? sel : this.editorcore.doc.body;
24006             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24007             
24008         }
24009         // pick a menu that exists..
24010         var tn = sel.tagName.toUpperCase();
24011         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24012         
24013         tn = sel.tagName.toUpperCase();
24014         
24015         var lastSel = this.tb.selectedNode;
24016         
24017         this.tb.selectedNode = sel;
24018         
24019         // if current menu does not match..
24020         
24021         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24022                 
24023             this.tb.el.hide();
24024             ///console.log("show: " + tn);
24025             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24026             this.tb.el.show();
24027             // update name
24028             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
24029             
24030             
24031             // update attributes
24032             if (this.tb.fields) {
24033                 this.tb.fields.each(function(e) {
24034                     if (e.stylename) {
24035                         e.setValue(sel.style[e.stylename]);
24036                         return;
24037                     } 
24038                    e.setValue(sel.getAttribute(e.attrname));
24039                 });
24040             }
24041             
24042             var hasStyles = false;
24043             for(var i in this.styles) {
24044                 hasStyles = true;
24045                 break;
24046             }
24047             
24048             // update styles
24049             if (hasStyles) { 
24050                 var st = this.tb.fields.item(0);
24051                 
24052                 st.store.removeAll();
24053                
24054                 
24055                 var cn = sel.className.split(/\s+/);
24056                 
24057                 var avs = [];
24058                 if (this.styles['*']) {
24059                     
24060                     Roo.each(this.styles['*'], function(v) {
24061                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24062                     });
24063                 }
24064                 if (this.styles[tn]) { 
24065                     Roo.each(this.styles[tn], function(v) {
24066                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24067                     });
24068                 }
24069                 
24070                 st.store.loadData(avs);
24071                 st.collapse();
24072                 st.setValue(cn);
24073             }
24074             // flag our selected Node.
24075             this.tb.selectedNode = sel;
24076            
24077            
24078             Roo.menu.MenuMgr.hideAll();
24079
24080         }
24081         
24082         if (!updateFooter) {
24083             //this.footDisp.dom.innerHTML = ''; 
24084             return;
24085         }
24086         // update the footer
24087         //
24088         var html = '';
24089         
24090         this.footerEls = ans.reverse();
24091         Roo.each(this.footerEls, function(a,i) {
24092             if (!a) { return; }
24093             html += html.length ? ' &gt; '  :  '';
24094             
24095             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24096             
24097         });
24098        
24099         // 
24100         var sz = this.footDisp.up('td').getSize();
24101         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24102         this.footDisp.dom.style.marginLeft = '5px';
24103         
24104         this.footDisp.dom.style.overflow = 'hidden';
24105         
24106         this.footDisp.dom.innerHTML = html;
24107             
24108         //this.editorsyncValue();
24109     },
24110      
24111     
24112    
24113        
24114     // private
24115     onDestroy : function(){
24116         if(this.rendered){
24117             
24118             this.tb.items.each(function(item){
24119                 if(item.menu){
24120                     item.menu.removeAll();
24121                     if(item.menu.el){
24122                         item.menu.el.destroy();
24123                     }
24124                 }
24125                 item.destroy();
24126             });
24127              
24128         }
24129     },
24130     onFirstFocus: function() {
24131         // need to do this for all the toolbars..
24132         this.tb.items.each(function(item){
24133            item.enable();
24134         });
24135     },
24136     buildToolbar: function(tlist, nm)
24137     {
24138         var editor = this.editor;
24139         var editorcore = this.editorcore;
24140          // create a new element.
24141         var wdiv = editor.wrap.createChild({
24142                 tag: 'div'
24143             }, editor.wrap.dom.firstChild.nextSibling, true);
24144         
24145        
24146         var tb = new Roo.Toolbar(wdiv);
24147         // add the name..
24148         
24149         tb.add(nm+ ":&nbsp;");
24150         
24151         var styles = [];
24152         for(var i in this.styles) {
24153             styles.push(i);
24154         }
24155         
24156         // styles...
24157         if (styles && styles.length) {
24158             
24159             // this needs a multi-select checkbox...
24160             tb.addField( new Roo.form.ComboBox({
24161                 store: new Roo.data.SimpleStore({
24162                     id : 'val',
24163                     fields: ['val', 'selected'],
24164                     data : [] 
24165                 }),
24166                 name : '-roo-edit-className',
24167                 attrname : 'className',
24168                 displayField: 'val',
24169                 typeAhead: false,
24170                 mode: 'local',
24171                 editable : false,
24172                 triggerAction: 'all',
24173                 emptyText:'Select Style',
24174                 selectOnFocus:true,
24175                 width: 130,
24176                 listeners : {
24177                     'select': function(c, r, i) {
24178                         // initial support only for on class per el..
24179                         tb.selectedNode.className =  r ? r.get('val') : '';
24180                         editorcore.syncValue();
24181                     }
24182                 }
24183     
24184             }));
24185         }
24186         
24187         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24188         var tbops = tbc.options;
24189         
24190         for (var i in tlist) {
24191             
24192             var item = tlist[i];
24193             tb.add(item.title + ":&nbsp;");
24194             
24195             
24196             //optname == used so you can configure the options available..
24197             var opts = item.opts ? item.opts : false;
24198             if (item.optname) {
24199                 opts = tbops[item.optname];
24200            
24201             }
24202             
24203             if (opts) {
24204                 // opts == pulldown..
24205                 tb.addField( new Roo.form.ComboBox({
24206                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24207                         id : 'val',
24208                         fields: ['val', 'display'],
24209                         data : opts  
24210                     }),
24211                     name : '-roo-edit-' + i,
24212                     attrname : i,
24213                     stylename : item.style ? item.style : false,
24214                     displayField: item.displayField ? item.displayField : 'val',
24215                     valueField :  'val',
24216                     typeAhead: false,
24217                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24218                     editable : false,
24219                     triggerAction: 'all',
24220                     emptyText:'Select',
24221                     selectOnFocus:true,
24222                     width: item.width ? item.width  : 130,
24223                     listeners : {
24224                         'select': function(c, r, i) {
24225                             if (c.stylename) {
24226                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24227                                 return;
24228                             }
24229                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24230                         }
24231                     }
24232
24233                 }));
24234                 continue;
24235                     
24236                  
24237                 
24238                 tb.addField( new Roo.form.TextField({
24239                     name: i,
24240                     width: 100,
24241                     //allowBlank:false,
24242                     value: ''
24243                 }));
24244                 continue;
24245             }
24246             tb.addField( new Roo.form.TextField({
24247                 name: '-roo-edit-' + i,
24248                 attrname : i,
24249                 
24250                 width: item.width,
24251                 //allowBlank:true,
24252                 value: '',
24253                 listeners: {
24254                     'change' : function(f, nv, ov) {
24255                         tb.selectedNode.setAttribute(f.attrname, nv);
24256                         editorcore.syncValue();
24257                     }
24258                 }
24259             }));
24260              
24261         }
24262         
24263         var _this = this;
24264         
24265         if(nm == 'BODY'){
24266             tb.addSeparator();
24267         
24268             tb.addButton( {
24269                 text: 'Stylesheets',
24270
24271                 listeners : {
24272                     click : function ()
24273                     {
24274                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24275                     }
24276                 }
24277             });
24278         }
24279         
24280         tb.addFill();
24281         tb.addButton( {
24282             text: 'Remove Tag',
24283     
24284             listeners : {
24285                 click : function ()
24286                 {
24287                     // remove
24288                     // undo does not work.
24289                      
24290                     var sn = tb.selectedNode;
24291                     
24292                     var pn = sn.parentNode;
24293                     
24294                     var stn =  sn.childNodes[0];
24295                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24296                     while (sn.childNodes.length) {
24297                         var node = sn.childNodes[0];
24298                         sn.removeChild(node);
24299                         //Roo.log(node);
24300                         pn.insertBefore(node, sn);
24301                         
24302                     }
24303                     pn.removeChild(sn);
24304                     var range = editorcore.createRange();
24305         
24306                     range.setStart(stn,0);
24307                     range.setEnd(en,0); //????
24308                     //range.selectNode(sel);
24309                     
24310                     
24311                     var selection = editorcore.getSelection();
24312                     selection.removeAllRanges();
24313                     selection.addRange(range);
24314                     
24315                     
24316                     
24317                     //_this.updateToolbar(null, null, pn);
24318                     _this.updateToolbar(null, null, null);
24319                     _this.footDisp.dom.innerHTML = ''; 
24320                 }
24321             }
24322             
24323                     
24324                 
24325             
24326         });
24327         
24328         
24329         tb.el.on('click', function(e){
24330             e.preventDefault(); // what does this do?
24331         });
24332         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24333         tb.el.hide();
24334         tb.name = nm;
24335         // dont need to disable them... as they will get hidden
24336         return tb;
24337          
24338         
24339     },
24340     buildFooter : function()
24341     {
24342         
24343         var fel = this.editor.wrap.createChild();
24344         this.footer = new Roo.Toolbar(fel);
24345         // toolbar has scrolly on left / right?
24346         var footDisp= new Roo.Toolbar.Fill();
24347         var _t = this;
24348         this.footer.add(
24349             {
24350                 text : '&lt;',
24351                 xtype: 'Button',
24352                 handler : function() {
24353                     _t.footDisp.scrollTo('left',0,true)
24354                 }
24355             }
24356         );
24357         this.footer.add( footDisp );
24358         this.footer.add( 
24359             {
24360                 text : '&gt;',
24361                 xtype: 'Button',
24362                 handler : function() {
24363                     // no animation..
24364                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24365                 }
24366             }
24367         );
24368         var fel = Roo.get(footDisp.el);
24369         fel.addClass('x-editor-context');
24370         this.footDispWrap = fel; 
24371         this.footDispWrap.overflow  = 'hidden';
24372         
24373         this.footDisp = fel.createChild();
24374         this.footDispWrap.on('click', this.onContextClick, this)
24375         
24376         
24377     },
24378     onContextClick : function (ev,dom)
24379     {
24380         ev.preventDefault();
24381         var  cn = dom.className;
24382         //Roo.log(cn);
24383         if (!cn.match(/x-ed-loc-/)) {
24384             return;
24385         }
24386         var n = cn.split('-').pop();
24387         var ans = this.footerEls;
24388         var sel = ans[n];
24389         
24390          // pick
24391         var range = this.editorcore.createRange();
24392         
24393         range.selectNodeContents(sel);
24394         //range.selectNode(sel);
24395         
24396         
24397         var selection = this.editorcore.getSelection();
24398         selection.removeAllRanges();
24399         selection.addRange(range);
24400         
24401         
24402         
24403         this.updateToolbar(null, null, sel);
24404         
24405         
24406     }
24407     
24408     
24409     
24410     
24411     
24412 });
24413
24414
24415
24416
24417
24418 /*
24419  * Based on:
24420  * Ext JS Library 1.1.1
24421  * Copyright(c) 2006-2007, Ext JS, LLC.
24422  *
24423  * Originally Released Under LGPL - original licence link has changed is not relivant.
24424  *
24425  * Fork - LGPL
24426  * <script type="text/javascript">
24427  */
24428  
24429 /**
24430  * @class Roo.form.BasicForm
24431  * @extends Roo.util.Observable
24432  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24433  * @constructor
24434  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24435  * @param {Object} config Configuration options
24436  */
24437 Roo.form.BasicForm = function(el, config){
24438     this.allItems = [];
24439     this.childForms = [];
24440     Roo.apply(this, config);
24441     /*
24442      * The Roo.form.Field items in this form.
24443      * @type MixedCollection
24444      */
24445      
24446      
24447     this.items = new Roo.util.MixedCollection(false, function(o){
24448         return o.id || (o.id = Roo.id());
24449     });
24450     this.addEvents({
24451         /**
24452          * @event beforeaction
24453          * Fires before any action is performed. Return false to cancel the action.
24454          * @param {Form} this
24455          * @param {Action} action The action to be performed
24456          */
24457         beforeaction: true,
24458         /**
24459          * @event actionfailed
24460          * Fires when an action fails.
24461          * @param {Form} this
24462          * @param {Action} action The action that failed
24463          */
24464         actionfailed : true,
24465         /**
24466          * @event actioncomplete
24467          * Fires when an action is completed.
24468          * @param {Form} this
24469          * @param {Action} action The action that completed
24470          */
24471         actioncomplete : true
24472     });
24473     if(el){
24474         this.initEl(el);
24475     }
24476     Roo.form.BasicForm.superclass.constructor.call(this);
24477     
24478     Roo.form.BasicForm.popover.apply();
24479 };
24480
24481 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24482     /**
24483      * @cfg {String} method
24484      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24485      */
24486     /**
24487      * @cfg {DataReader} reader
24488      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24489      * This is optional as there is built-in support for processing JSON.
24490      */
24491     /**
24492      * @cfg {DataReader} errorReader
24493      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24494      * This is completely optional as there is built-in support for processing JSON.
24495      */
24496     /**
24497      * @cfg {String} url
24498      * The URL to use for form actions if one isn't supplied in the action options.
24499      */
24500     /**
24501      * @cfg {Boolean} fileUpload
24502      * Set to true if this form is a file upload.
24503      */
24504      
24505     /**
24506      * @cfg {Object} baseParams
24507      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24508      */
24509      /**
24510      
24511     /**
24512      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24513      */
24514     timeout: 30,
24515
24516     // private
24517     activeAction : null,
24518
24519     /**
24520      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24521      * or setValues() data instead of when the form was first created.
24522      */
24523     trackResetOnLoad : false,
24524     
24525     
24526     /**
24527      * childForms - used for multi-tab forms
24528      * @type {Array}
24529      */
24530     childForms : false,
24531     
24532     /**
24533      * allItems - full list of fields.
24534      * @type {Array}
24535      */
24536     allItems : false,
24537     
24538     /**
24539      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24540      * element by passing it or its id or mask the form itself by passing in true.
24541      * @type Mixed
24542      */
24543     waitMsgTarget : false,
24544     
24545     /**
24546      * @type Boolean
24547      */
24548     disableMask : false,
24549     
24550     /**
24551      * @cfg {Boolean} errorMask (true|false) default false
24552      */
24553     errorMask : false,
24554     
24555     /**
24556      * @cfg {Number} maskOffset Default 100
24557      */
24558     maskOffset : 100,
24559
24560     // private
24561     initEl : function(el){
24562         this.el = Roo.get(el);
24563         this.id = this.el.id || Roo.id();
24564         this.el.on('submit', this.onSubmit, this);
24565         this.el.addClass('x-form');
24566     },
24567
24568     // private
24569     onSubmit : function(e){
24570         e.stopEvent();
24571     },
24572
24573     /**
24574      * Returns true if client-side validation on the form is successful.
24575      * @return Boolean
24576      */
24577     isValid : function(){
24578         var valid = true;
24579         var target = false;
24580         this.items.each(function(f){
24581             if(f.validate()){
24582                 return;
24583             }
24584             
24585             valid = false;
24586                 
24587             if(!target && f.el.isVisible(true)){
24588                 target = f;
24589             }
24590         });
24591         
24592         if(this.errorMask && !valid){
24593             Roo.form.BasicForm.popover.mask(this, target);
24594         }
24595         
24596         return valid;
24597     },
24598
24599     /**
24600      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24601      * @return Boolean
24602      */
24603     isDirty : function(){
24604         var dirty = false;
24605         this.items.each(function(f){
24606            if(f.isDirty()){
24607                dirty = true;
24608                return false;
24609            }
24610         });
24611         return dirty;
24612     },
24613     
24614     /**
24615      * Returns true if any fields in this form have changed since their original load. (New version)
24616      * @return Boolean
24617      */
24618     
24619     hasChanged : function()
24620     {
24621         var dirty = false;
24622         this.items.each(function(f){
24623            if(f.hasChanged()){
24624                dirty = true;
24625                return false;
24626            }
24627         });
24628         return dirty;
24629         
24630     },
24631     /**
24632      * Resets all hasChanged to 'false' -
24633      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24634      * So hasChanged storage is only to be used for this purpose
24635      * @return Boolean
24636      */
24637     resetHasChanged : function()
24638     {
24639         this.items.each(function(f){
24640            f.resetHasChanged();
24641         });
24642         
24643     },
24644     
24645     
24646     /**
24647      * Performs a predefined action (submit or load) or custom actions you define on this form.
24648      * @param {String} actionName The name of the action type
24649      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24650      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24651      * accept other config options):
24652      * <pre>
24653 Property          Type             Description
24654 ----------------  ---------------  ----------------------------------------------------------------------------------
24655 url               String           The url for the action (defaults to the form's url)
24656 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24657 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24658 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24659                                    validate the form on the client (defaults to false)
24660      * </pre>
24661      * @return {BasicForm} this
24662      */
24663     doAction : function(action, options){
24664         if(typeof action == 'string'){
24665             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24666         }
24667         if(this.fireEvent('beforeaction', this, action) !== false){
24668             this.beforeAction(action);
24669             action.run.defer(100, action);
24670         }
24671         return this;
24672     },
24673
24674     /**
24675      * Shortcut to do a submit action.
24676      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24677      * @return {BasicForm} this
24678      */
24679     submit : function(options){
24680         this.doAction('submit', options);
24681         return this;
24682     },
24683
24684     /**
24685      * Shortcut to do a load action.
24686      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24687      * @return {BasicForm} this
24688      */
24689     load : function(options){
24690         this.doAction('load', options);
24691         return this;
24692     },
24693
24694     /**
24695      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24696      * @param {Record} record The record to edit
24697      * @return {BasicForm} this
24698      */
24699     updateRecord : function(record){
24700         record.beginEdit();
24701         var fs = record.fields;
24702         fs.each(function(f){
24703             var field = this.findField(f.name);
24704             if(field){
24705                 record.set(f.name, field.getValue());
24706             }
24707         }, this);
24708         record.endEdit();
24709         return this;
24710     },
24711
24712     /**
24713      * Loads an Roo.data.Record into this form.
24714      * @param {Record} record The record to load
24715      * @return {BasicForm} this
24716      */
24717     loadRecord : function(record){
24718         this.setValues(record.data);
24719         return this;
24720     },
24721
24722     // private
24723     beforeAction : function(action){
24724         var o = action.options;
24725         
24726         if(!this.disableMask) {
24727             if(this.waitMsgTarget === true){
24728                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24729             }else if(this.waitMsgTarget){
24730                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24731                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24732             }else {
24733                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24734             }
24735         }
24736         
24737          
24738     },
24739
24740     // private
24741     afterAction : function(action, success){
24742         this.activeAction = null;
24743         var o = action.options;
24744         
24745         if(!this.disableMask) {
24746             if(this.waitMsgTarget === true){
24747                 this.el.unmask();
24748             }else if(this.waitMsgTarget){
24749                 this.waitMsgTarget.unmask();
24750             }else{
24751                 Roo.MessageBox.updateProgress(1);
24752                 Roo.MessageBox.hide();
24753             }
24754         }
24755         
24756         if(success){
24757             if(o.reset){
24758                 this.reset();
24759             }
24760             Roo.callback(o.success, o.scope, [this, action]);
24761             this.fireEvent('actioncomplete', this, action);
24762             
24763         }else{
24764             
24765             // failure condition..
24766             // we have a scenario where updates need confirming.
24767             // eg. if a locking scenario exists..
24768             // we look for { errors : { needs_confirm : true }} in the response.
24769             if (
24770                 (typeof(action.result) != 'undefined')  &&
24771                 (typeof(action.result.errors) != 'undefined')  &&
24772                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24773            ){
24774                 var _t = this;
24775                 Roo.MessageBox.confirm(
24776                     "Change requires confirmation",
24777                     action.result.errorMsg,
24778                     function(r) {
24779                         if (r != 'yes') {
24780                             return;
24781                         }
24782                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24783                     }
24784                     
24785                 );
24786                 
24787                 
24788                 
24789                 return;
24790             }
24791             
24792             Roo.callback(o.failure, o.scope, [this, action]);
24793             // show an error message if no failed handler is set..
24794             if (!this.hasListener('actionfailed')) {
24795                 Roo.MessageBox.alert("Error",
24796                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24797                         action.result.errorMsg :
24798                         "Saving Failed, please check your entries or try again"
24799                 );
24800             }
24801             
24802             this.fireEvent('actionfailed', this, action);
24803         }
24804         
24805     },
24806
24807     /**
24808      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24809      * @param {String} id The value to search for
24810      * @return Field
24811      */
24812     findField : function(id){
24813         var field = this.items.get(id);
24814         if(!field){
24815             this.items.each(function(f){
24816                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24817                     field = f;
24818                     return false;
24819                 }
24820             });
24821         }
24822         return field || null;
24823     },
24824
24825     /**
24826      * Add a secondary form to this one, 
24827      * Used to provide tabbed forms. One form is primary, with hidden values 
24828      * which mirror the elements from the other forms.
24829      * 
24830      * @param {Roo.form.Form} form to add.
24831      * 
24832      */
24833     addForm : function(form)
24834     {
24835        
24836         if (this.childForms.indexOf(form) > -1) {
24837             // already added..
24838             return;
24839         }
24840         this.childForms.push(form);
24841         var n = '';
24842         Roo.each(form.allItems, function (fe) {
24843             
24844             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24845             if (this.findField(n)) { // already added..
24846                 return;
24847             }
24848             var add = new Roo.form.Hidden({
24849                 name : n
24850             });
24851             add.render(this.el);
24852             
24853             this.add( add );
24854         }, this);
24855         
24856     },
24857     /**
24858      * Mark fields in this form invalid in bulk.
24859      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24860      * @return {BasicForm} this
24861      */
24862     markInvalid : function(errors){
24863         if(errors instanceof Array){
24864             for(var i = 0, len = errors.length; i < len; i++){
24865                 var fieldError = errors[i];
24866                 var f = this.findField(fieldError.id);
24867                 if(f){
24868                     f.markInvalid(fieldError.msg);
24869                 }
24870             }
24871         }else{
24872             var field, id;
24873             for(id in errors){
24874                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24875                     field.markInvalid(errors[id]);
24876                 }
24877             }
24878         }
24879         Roo.each(this.childForms || [], function (f) {
24880             f.markInvalid(errors);
24881         });
24882         
24883         return this;
24884     },
24885
24886     /**
24887      * Set values for fields in this form in bulk.
24888      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24889      * @return {BasicForm} this
24890      */
24891     setValues : function(values){
24892         if(values instanceof Array){ // array of objects
24893             for(var i = 0, len = values.length; i < len; i++){
24894                 var v = values[i];
24895                 var f = this.findField(v.id);
24896                 if(f){
24897                     f.setValue(v.value);
24898                     if(this.trackResetOnLoad){
24899                         f.originalValue = f.getValue();
24900                     }
24901                 }
24902             }
24903         }else{ // object hash
24904             var field, id;
24905             for(id in values){
24906                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24907                     
24908                     if (field.setFromData && 
24909                         field.valueField && 
24910                         field.displayField &&
24911                         // combos' with local stores can 
24912                         // be queried via setValue()
24913                         // to set their value..
24914                         (field.store && !field.store.isLocal)
24915                         ) {
24916                         // it's a combo
24917                         var sd = { };
24918                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24919                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24920                         field.setFromData(sd);
24921                         
24922                     } else {
24923                         field.setValue(values[id]);
24924                     }
24925                     
24926                     
24927                     if(this.trackResetOnLoad){
24928                         field.originalValue = field.getValue();
24929                     }
24930                 }
24931             }
24932         }
24933         this.resetHasChanged();
24934         
24935         
24936         Roo.each(this.childForms || [], function (f) {
24937             f.setValues(values);
24938             f.resetHasChanged();
24939         });
24940                 
24941         return this;
24942     },
24943  
24944     /**
24945      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24946      * they are returned as an array.
24947      * @param {Boolean} asString
24948      * @return {Object}
24949      */
24950     getValues : function(asString){
24951         if (this.childForms) {
24952             // copy values from the child forms
24953             Roo.each(this.childForms, function (f) {
24954                 this.setValues(f.getValues());
24955             }, this);
24956         }
24957         
24958         // use formdata
24959         if (typeof(FormData) != 'undefined' && asString !== true) {
24960             // this relies on a 'recent' version of chrome apparently...
24961             try {
24962                 var fd = (new FormData(this.el.dom)).entries();
24963                 var ret = {};
24964                 var ent = fd.next();
24965                 while (!ent.done) {
24966                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
24967                     ent = fd.next();
24968                 };
24969                 return ret;
24970             } catch(e) {
24971                 
24972             }
24973             
24974         }
24975         
24976         
24977         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
24978         if(asString === true){
24979             return fs;
24980         }
24981         return Roo.urlDecode(fs);
24982     },
24983     
24984     /**
24985      * Returns the fields in this form as an object with key/value pairs. 
24986      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
24987      * @return {Object}
24988      */
24989     getFieldValues : function(with_hidden)
24990     {
24991         if (this.childForms) {
24992             // copy values from the child forms
24993             // should this call getFieldValues - probably not as we do not currently copy
24994             // hidden fields when we generate..
24995             Roo.each(this.childForms, function (f) {
24996                 this.setValues(f.getValues());
24997             }, this);
24998         }
24999         
25000         var ret = {};
25001         this.items.each(function(f){
25002             if (!f.getName()) {
25003                 return;
25004             }
25005             var v = f.getValue();
25006             if (f.inputType =='radio') {
25007                 if (typeof(ret[f.getName()]) == 'undefined') {
25008                     ret[f.getName()] = ''; // empty..
25009                 }
25010                 
25011                 if (!f.el.dom.checked) {
25012                     return;
25013                     
25014                 }
25015                 v = f.el.dom.value;
25016                 
25017             }
25018             
25019             // not sure if this supported any more..
25020             if ((typeof(v) == 'object') && f.getRawValue) {
25021                 v = f.getRawValue() ; // dates..
25022             }
25023             // combo boxes where name != hiddenName...
25024             if (f.name != f.getName()) {
25025                 ret[f.name] = f.getRawValue();
25026             }
25027             ret[f.getName()] = v;
25028         });
25029         
25030         return ret;
25031     },
25032
25033     /**
25034      * Clears all invalid messages in this form.
25035      * @return {BasicForm} this
25036      */
25037     clearInvalid : function(){
25038         this.items.each(function(f){
25039            f.clearInvalid();
25040         });
25041         
25042         Roo.each(this.childForms || [], function (f) {
25043             f.clearInvalid();
25044         });
25045         
25046         
25047         return this;
25048     },
25049
25050     /**
25051      * Resets this form.
25052      * @return {BasicForm} this
25053      */
25054     reset : function(){
25055         this.items.each(function(f){
25056             f.reset();
25057         });
25058         
25059         Roo.each(this.childForms || [], function (f) {
25060             f.reset();
25061         });
25062         this.resetHasChanged();
25063         
25064         return this;
25065     },
25066
25067     /**
25068      * Add Roo.form components to this form.
25069      * @param {Field} field1
25070      * @param {Field} field2 (optional)
25071      * @param {Field} etc (optional)
25072      * @return {BasicForm} this
25073      */
25074     add : function(){
25075         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25076         return this;
25077     },
25078
25079
25080     /**
25081      * Removes a field from the items collection (does NOT remove its markup).
25082      * @param {Field} field
25083      * @return {BasicForm} this
25084      */
25085     remove : function(field){
25086         this.items.remove(field);
25087         return this;
25088     },
25089
25090     /**
25091      * Looks at the fields in this form, checks them for an id attribute,
25092      * and calls applyTo on the existing dom element with that id.
25093      * @return {BasicForm} this
25094      */
25095     render : function(){
25096         this.items.each(function(f){
25097             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25098                 f.applyTo(f.id);
25099             }
25100         });
25101         return this;
25102     },
25103
25104     /**
25105      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25106      * @param {Object} values
25107      * @return {BasicForm} this
25108      */
25109     applyToFields : function(o){
25110         this.items.each(function(f){
25111            Roo.apply(f, o);
25112         });
25113         return this;
25114     },
25115
25116     /**
25117      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25118      * @param {Object} values
25119      * @return {BasicForm} this
25120      */
25121     applyIfToFields : function(o){
25122         this.items.each(function(f){
25123            Roo.applyIf(f, o);
25124         });
25125         return this;
25126     }
25127 });
25128
25129 // back compat
25130 Roo.BasicForm = Roo.form.BasicForm;
25131
25132 Roo.apply(Roo.form.BasicForm, {
25133     
25134     popover : {
25135         
25136         padding : 5,
25137         
25138         isApplied : false,
25139         
25140         isMasked : false,
25141         
25142         form : false,
25143         
25144         target : false,
25145         
25146         intervalID : false,
25147         
25148         maskEl : false,
25149         
25150         apply : function()
25151         {
25152             if(this.isApplied){
25153                 return;
25154             }
25155             
25156             this.maskEl = {
25157                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25158                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25159                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25160                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25161             };
25162             
25163             this.maskEl.top.enableDisplayMode("block");
25164             this.maskEl.left.enableDisplayMode("block");
25165             this.maskEl.bottom.enableDisplayMode("block");
25166             this.maskEl.right.enableDisplayMode("block");
25167             
25168             Roo.get(document.body).on('click', function(){
25169                 this.unmask();
25170             }, this);
25171             
25172             Roo.get(document.body).on('touchstart', function(){
25173                 this.unmask();
25174             }, this);
25175             
25176             this.isApplied = true
25177         },
25178         
25179         mask : function(form, target)
25180         {
25181             this.form = form;
25182             
25183             this.target = target;
25184             
25185             if(!this.form.errorMask || !target.el){
25186                 return;
25187             }
25188             
25189             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25190             
25191             var ot = this.target.el.calcOffsetsTo(scrollable);
25192             
25193             var scrollTo = ot[1] - this.form.maskOffset;
25194             
25195             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25196             
25197             scrollable.scrollTo('top', scrollTo);
25198             
25199             var el = this.target.wrap || this.target.el;
25200             
25201             var box = el.getBox();
25202             
25203             this.maskEl.top.setStyle('position', 'absolute');
25204             this.maskEl.top.setStyle('z-index', 10000);
25205             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25206             this.maskEl.top.setLeft(0);
25207             this.maskEl.top.setTop(0);
25208             this.maskEl.top.show();
25209             
25210             this.maskEl.left.setStyle('position', 'absolute');
25211             this.maskEl.left.setStyle('z-index', 10000);
25212             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25213             this.maskEl.left.setLeft(0);
25214             this.maskEl.left.setTop(box.y - this.padding);
25215             this.maskEl.left.show();
25216
25217             this.maskEl.bottom.setStyle('position', 'absolute');
25218             this.maskEl.bottom.setStyle('z-index', 10000);
25219             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25220             this.maskEl.bottom.setLeft(0);
25221             this.maskEl.bottom.setTop(box.bottom + this.padding);
25222             this.maskEl.bottom.show();
25223
25224             this.maskEl.right.setStyle('position', 'absolute');
25225             this.maskEl.right.setStyle('z-index', 10000);
25226             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25227             this.maskEl.right.setLeft(box.right + this.padding);
25228             this.maskEl.right.setTop(box.y - this.padding);
25229             this.maskEl.right.show();
25230
25231             this.intervalID = window.setInterval(function() {
25232                 Roo.form.BasicForm.popover.unmask();
25233             }, 10000);
25234
25235             window.onwheel = function(){ return false;};
25236             
25237             (function(){ this.isMasked = true; }).defer(500, this);
25238             
25239         },
25240         
25241         unmask : function()
25242         {
25243             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25244                 return;
25245             }
25246             
25247             this.maskEl.top.setStyle('position', 'absolute');
25248             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25249             this.maskEl.top.hide();
25250
25251             this.maskEl.left.setStyle('position', 'absolute');
25252             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25253             this.maskEl.left.hide();
25254
25255             this.maskEl.bottom.setStyle('position', 'absolute');
25256             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25257             this.maskEl.bottom.hide();
25258
25259             this.maskEl.right.setStyle('position', 'absolute');
25260             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25261             this.maskEl.right.hide();
25262             
25263             window.onwheel = function(){ return true;};
25264             
25265             if(this.intervalID){
25266                 window.clearInterval(this.intervalID);
25267                 this.intervalID = false;
25268             }
25269             
25270             this.isMasked = false;
25271             
25272         }
25273         
25274     }
25275     
25276 });/*
25277  * Based on:
25278  * Ext JS Library 1.1.1
25279  * Copyright(c) 2006-2007, Ext JS, LLC.
25280  *
25281  * Originally Released Under LGPL - original licence link has changed is not relivant.
25282  *
25283  * Fork - LGPL
25284  * <script type="text/javascript">
25285  */
25286
25287 /**
25288  * @class Roo.form.Form
25289  * @extends Roo.form.BasicForm
25290  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25291  * @constructor
25292  * @param {Object} config Configuration options
25293  */
25294 Roo.form.Form = function(config){
25295     var xitems =  [];
25296     if (config.items) {
25297         xitems = config.items;
25298         delete config.items;
25299     }
25300    
25301     
25302     Roo.form.Form.superclass.constructor.call(this, null, config);
25303     this.url = this.url || this.action;
25304     if(!this.root){
25305         this.root = new Roo.form.Layout(Roo.applyIf({
25306             id: Roo.id()
25307         }, config));
25308     }
25309     this.active = this.root;
25310     /**
25311      * Array of all the buttons that have been added to this form via {@link addButton}
25312      * @type Array
25313      */
25314     this.buttons = [];
25315     this.allItems = [];
25316     this.addEvents({
25317         /**
25318          * @event clientvalidation
25319          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25320          * @param {Form} this
25321          * @param {Boolean} valid true if the form has passed client-side validation
25322          */
25323         clientvalidation: true,
25324         /**
25325          * @event rendered
25326          * Fires when the form is rendered
25327          * @param {Roo.form.Form} form
25328          */
25329         rendered : true
25330     });
25331     
25332     if (this.progressUrl) {
25333             // push a hidden field onto the list of fields..
25334             this.addxtype( {
25335                     xns: Roo.form, 
25336                     xtype : 'Hidden', 
25337                     name : 'UPLOAD_IDENTIFIER' 
25338             });
25339         }
25340         
25341     
25342     Roo.each(xitems, this.addxtype, this);
25343     
25344 };
25345
25346 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25347     /**
25348      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25349      */
25350     /**
25351      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25352      */
25353     /**
25354      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25355      */
25356     buttonAlign:'center',
25357
25358     /**
25359      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25360      */
25361     minButtonWidth:75,
25362
25363     /**
25364      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25365      * This property cascades to child containers if not set.
25366      */
25367     labelAlign:'left',
25368
25369     /**
25370      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25371      * fires a looping event with that state. This is required to bind buttons to the valid
25372      * state using the config value formBind:true on the button.
25373      */
25374     monitorValid : false,
25375
25376     /**
25377      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25378      */
25379     monitorPoll : 200,
25380     
25381     /**
25382      * @cfg {String} progressUrl - Url to return progress data 
25383      */
25384     
25385     progressUrl : false,
25386     /**
25387      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25388      * sending a formdata with extra parameters - eg uploaded elements.
25389      */
25390     
25391     formData : false,
25392     
25393     /**
25394      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25395      * fields are added and the column is closed. If no fields are passed the column remains open
25396      * until end() is called.
25397      * @param {Object} config The config to pass to the column
25398      * @param {Field} field1 (optional)
25399      * @param {Field} field2 (optional)
25400      * @param {Field} etc (optional)
25401      * @return Column The column container object
25402      */
25403     column : function(c){
25404         var col = new Roo.form.Column(c);
25405         this.start(col);
25406         if(arguments.length > 1){ // duplicate code required because of Opera
25407             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25408             this.end();
25409         }
25410         return col;
25411     },
25412
25413     /**
25414      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25415      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25416      * until end() is called.
25417      * @param {Object} config The config to pass to the fieldset
25418      * @param {Field} field1 (optional)
25419      * @param {Field} field2 (optional)
25420      * @param {Field} etc (optional)
25421      * @return FieldSet The fieldset container object
25422      */
25423     fieldset : function(c){
25424         var fs = new Roo.form.FieldSet(c);
25425         this.start(fs);
25426         if(arguments.length > 1){ // duplicate code required because of Opera
25427             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25428             this.end();
25429         }
25430         return fs;
25431     },
25432
25433     /**
25434      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25435      * fields are added and the container is closed. If no fields are passed the container remains open
25436      * until end() is called.
25437      * @param {Object} config The config to pass to the Layout
25438      * @param {Field} field1 (optional)
25439      * @param {Field} field2 (optional)
25440      * @param {Field} etc (optional)
25441      * @return Layout The container object
25442      */
25443     container : function(c){
25444         var l = new Roo.form.Layout(c);
25445         this.start(l);
25446         if(arguments.length > 1){ // duplicate code required because of Opera
25447             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25448             this.end();
25449         }
25450         return l;
25451     },
25452
25453     /**
25454      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25455      * @param {Object} container A Roo.form.Layout or subclass of Layout
25456      * @return {Form} this
25457      */
25458     start : function(c){
25459         // cascade label info
25460         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25461         this.active.stack.push(c);
25462         c.ownerCt = this.active;
25463         this.active = c;
25464         return this;
25465     },
25466
25467     /**
25468      * Closes the current open container
25469      * @return {Form} this
25470      */
25471     end : function(){
25472         if(this.active == this.root){
25473             return this;
25474         }
25475         this.active = this.active.ownerCt;
25476         return this;
25477     },
25478
25479     /**
25480      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25481      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25482      * as the label of the field.
25483      * @param {Field} field1
25484      * @param {Field} field2 (optional)
25485      * @param {Field} etc. (optional)
25486      * @return {Form} this
25487      */
25488     add : function(){
25489         this.active.stack.push.apply(this.active.stack, arguments);
25490         this.allItems.push.apply(this.allItems,arguments);
25491         var r = [];
25492         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25493             if(a[i].isFormField){
25494                 r.push(a[i]);
25495             }
25496         }
25497         if(r.length > 0){
25498             Roo.form.Form.superclass.add.apply(this, r);
25499         }
25500         return this;
25501     },
25502     
25503
25504     
25505     
25506     
25507      /**
25508      * Find any element that has been added to a form, using it's ID or name
25509      * This can include framesets, columns etc. along with regular fields..
25510      * @param {String} id - id or name to find.
25511      
25512      * @return {Element} e - or false if nothing found.
25513      */
25514     findbyId : function(id)
25515     {
25516         var ret = false;
25517         if (!id) {
25518             return ret;
25519         }
25520         Roo.each(this.allItems, function(f){
25521             if (f.id == id || f.name == id ){
25522                 ret = f;
25523                 return false;
25524             }
25525         });
25526         return ret;
25527     },
25528
25529     
25530     
25531     /**
25532      * Render this form into the passed container. This should only be called once!
25533      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25534      * @return {Form} this
25535      */
25536     render : function(ct)
25537     {
25538         
25539         
25540         
25541         ct = Roo.get(ct);
25542         var o = this.autoCreate || {
25543             tag: 'form',
25544             method : this.method || 'POST',
25545             id : this.id || Roo.id()
25546         };
25547         this.initEl(ct.createChild(o));
25548
25549         this.root.render(this.el);
25550         
25551        
25552              
25553         this.items.each(function(f){
25554             f.render('x-form-el-'+f.id);
25555         });
25556
25557         if(this.buttons.length > 0){
25558             // tables are required to maintain order and for correct IE layout
25559             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25560                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25561                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25562             }}, null, true);
25563             var tr = tb.getElementsByTagName('tr')[0];
25564             for(var i = 0, len = this.buttons.length; i < len; i++) {
25565                 var b = this.buttons[i];
25566                 var td = document.createElement('td');
25567                 td.className = 'x-form-btn-td';
25568                 b.render(tr.appendChild(td));
25569             }
25570         }
25571         if(this.monitorValid){ // initialize after render
25572             this.startMonitoring();
25573         }
25574         this.fireEvent('rendered', this);
25575         return this;
25576     },
25577
25578     /**
25579      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25580      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25581      * object or a valid Roo.DomHelper element config
25582      * @param {Function} handler The function called when the button is clicked
25583      * @param {Object} scope (optional) The scope of the handler function
25584      * @return {Roo.Button}
25585      */
25586     addButton : function(config, handler, scope){
25587         var bc = {
25588             handler: handler,
25589             scope: scope,
25590             minWidth: this.minButtonWidth,
25591             hideParent:true
25592         };
25593         if(typeof config == "string"){
25594             bc.text = config;
25595         }else{
25596             Roo.apply(bc, config);
25597         }
25598         var btn = new Roo.Button(null, bc);
25599         this.buttons.push(btn);
25600         return btn;
25601     },
25602
25603      /**
25604      * Adds a series of form elements (using the xtype property as the factory method.
25605      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25606      * @param {Object} config 
25607      */
25608     
25609     addxtype : function()
25610     {
25611         var ar = Array.prototype.slice.call(arguments, 0);
25612         var ret = false;
25613         for(var i = 0; i < ar.length; i++) {
25614             if (!ar[i]) {
25615                 continue; // skip -- if this happends something invalid got sent, we 
25616                 // should ignore it, as basically that interface element will not show up
25617                 // and that should be pretty obvious!!
25618             }
25619             
25620             if (Roo.form[ar[i].xtype]) {
25621                 ar[i].form = this;
25622                 var fe = Roo.factory(ar[i], Roo.form);
25623                 if (!ret) {
25624                     ret = fe;
25625                 }
25626                 fe.form = this;
25627                 if (fe.store) {
25628                     fe.store.form = this;
25629                 }
25630                 if (fe.isLayout) {  
25631                          
25632                     this.start(fe);
25633                     this.allItems.push(fe);
25634                     if (fe.items && fe.addxtype) {
25635                         fe.addxtype.apply(fe, fe.items);
25636                         delete fe.items;
25637                     }
25638                      this.end();
25639                     continue;
25640                 }
25641                 
25642                 
25643                  
25644                 this.add(fe);
25645               //  console.log('adding ' + ar[i].xtype);
25646             }
25647             if (ar[i].xtype == 'Button') {  
25648                 //console.log('adding button');
25649                 //console.log(ar[i]);
25650                 this.addButton(ar[i]);
25651                 this.allItems.push(fe);
25652                 continue;
25653             }
25654             
25655             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25656                 alert('end is not supported on xtype any more, use items');
25657             //    this.end();
25658             //    //console.log('adding end');
25659             }
25660             
25661         }
25662         return ret;
25663     },
25664     
25665     /**
25666      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25667      * option "monitorValid"
25668      */
25669     startMonitoring : function(){
25670         if(!this.bound){
25671             this.bound = true;
25672             Roo.TaskMgr.start({
25673                 run : this.bindHandler,
25674                 interval : this.monitorPoll || 200,
25675                 scope: this
25676             });
25677         }
25678     },
25679
25680     /**
25681      * Stops monitoring of the valid state of this form
25682      */
25683     stopMonitoring : function(){
25684         this.bound = false;
25685     },
25686
25687     // private
25688     bindHandler : function(){
25689         if(!this.bound){
25690             return false; // stops binding
25691         }
25692         var valid = true;
25693         this.items.each(function(f){
25694             if(!f.isValid(true)){
25695                 valid = false;
25696                 return false;
25697             }
25698         });
25699         for(var i = 0, len = this.buttons.length; i < len; i++){
25700             var btn = this.buttons[i];
25701             if(btn.formBind === true && btn.disabled === valid){
25702                 btn.setDisabled(!valid);
25703             }
25704         }
25705         this.fireEvent('clientvalidation', this, valid);
25706     }
25707     
25708     
25709     
25710     
25711     
25712     
25713     
25714     
25715 });
25716
25717
25718 // back compat
25719 Roo.Form = Roo.form.Form;
25720 /*
25721  * Based on:
25722  * Ext JS Library 1.1.1
25723  * Copyright(c) 2006-2007, Ext JS, LLC.
25724  *
25725  * Originally Released Under LGPL - original licence link has changed is not relivant.
25726  *
25727  * Fork - LGPL
25728  * <script type="text/javascript">
25729  */
25730
25731 // as we use this in bootstrap.
25732 Roo.namespace('Roo.form');
25733  /**
25734  * @class Roo.form.Action
25735  * Internal Class used to handle form actions
25736  * @constructor
25737  * @param {Roo.form.BasicForm} el The form element or its id
25738  * @param {Object} config Configuration options
25739  */
25740
25741  
25742  
25743 // define the action interface
25744 Roo.form.Action = function(form, options){
25745     this.form = form;
25746     this.options = options || {};
25747 };
25748 /**
25749  * Client Validation Failed
25750  * @const 
25751  */
25752 Roo.form.Action.CLIENT_INVALID = 'client';
25753 /**
25754  * Server Validation Failed
25755  * @const 
25756  */
25757 Roo.form.Action.SERVER_INVALID = 'server';
25758  /**
25759  * Connect to Server Failed
25760  * @const 
25761  */
25762 Roo.form.Action.CONNECT_FAILURE = 'connect';
25763 /**
25764  * Reading Data from Server Failed
25765  * @const 
25766  */
25767 Roo.form.Action.LOAD_FAILURE = 'load';
25768
25769 Roo.form.Action.prototype = {
25770     type : 'default',
25771     failureType : undefined,
25772     response : undefined,
25773     result : undefined,
25774
25775     // interface method
25776     run : function(options){
25777
25778     },
25779
25780     // interface method
25781     success : function(response){
25782
25783     },
25784
25785     // interface method
25786     handleResponse : function(response){
25787
25788     },
25789
25790     // default connection failure
25791     failure : function(response){
25792         
25793         this.response = response;
25794         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25795         this.form.afterAction(this, false);
25796     },
25797
25798     processResponse : function(response){
25799         this.response = response;
25800         if(!response.responseText){
25801             return true;
25802         }
25803         this.result = this.handleResponse(response);
25804         return this.result;
25805     },
25806
25807     // utility functions used internally
25808     getUrl : function(appendParams){
25809         var url = this.options.url || this.form.url || this.form.el.dom.action;
25810         if(appendParams){
25811             var p = this.getParams();
25812             if(p){
25813                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25814             }
25815         }
25816         return url;
25817     },
25818
25819     getMethod : function(){
25820         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25821     },
25822
25823     getParams : function(){
25824         var bp = this.form.baseParams;
25825         var p = this.options.params;
25826         if(p){
25827             if(typeof p == "object"){
25828                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25829             }else if(typeof p == 'string' && bp){
25830                 p += '&' + Roo.urlEncode(bp);
25831             }
25832         }else if(bp){
25833             p = Roo.urlEncode(bp);
25834         }
25835         return p;
25836     },
25837
25838     createCallback : function(){
25839         return {
25840             success: this.success,
25841             failure: this.failure,
25842             scope: this,
25843             timeout: (this.form.timeout*1000),
25844             upload: this.form.fileUpload ? this.success : undefined
25845         };
25846     }
25847 };
25848
25849 Roo.form.Action.Submit = function(form, options){
25850     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25851 };
25852
25853 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25854     type : 'submit',
25855
25856     haveProgress : false,
25857     uploadComplete : false,
25858     
25859     // uploadProgress indicator.
25860     uploadProgress : function()
25861     {
25862         if (!this.form.progressUrl) {
25863             return;
25864         }
25865         
25866         if (!this.haveProgress) {
25867             Roo.MessageBox.progress("Uploading", "Uploading");
25868         }
25869         if (this.uploadComplete) {
25870            Roo.MessageBox.hide();
25871            return;
25872         }
25873         
25874         this.haveProgress = true;
25875    
25876         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25877         
25878         var c = new Roo.data.Connection();
25879         c.request({
25880             url : this.form.progressUrl,
25881             params: {
25882                 id : uid
25883             },
25884             method: 'GET',
25885             success : function(req){
25886                //console.log(data);
25887                 var rdata = false;
25888                 var edata;
25889                 try  {
25890                    rdata = Roo.decode(req.responseText)
25891                 } catch (e) {
25892                     Roo.log("Invalid data from server..");
25893                     Roo.log(edata);
25894                     return;
25895                 }
25896                 if (!rdata || !rdata.success) {
25897                     Roo.log(rdata);
25898                     Roo.MessageBox.alert(Roo.encode(rdata));
25899                     return;
25900                 }
25901                 var data = rdata.data;
25902                 
25903                 if (this.uploadComplete) {
25904                    Roo.MessageBox.hide();
25905                    return;
25906                 }
25907                    
25908                 if (data){
25909                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25910                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25911                     );
25912                 }
25913                 this.uploadProgress.defer(2000,this);
25914             },
25915        
25916             failure: function(data) {
25917                 Roo.log('progress url failed ');
25918                 Roo.log(data);
25919             },
25920             scope : this
25921         });
25922            
25923     },
25924     
25925     
25926     run : function()
25927     {
25928         // run get Values on the form, so it syncs any secondary forms.
25929         this.form.getValues();
25930         
25931         var o = this.options;
25932         var method = this.getMethod();
25933         var isPost = method == 'POST';
25934         if(o.clientValidation === false || this.form.isValid()){
25935             
25936             if (this.form.progressUrl) {
25937                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25938                     (new Date() * 1) + '' + Math.random());
25939                     
25940             } 
25941             
25942             
25943             Roo.Ajax.request(Roo.apply(this.createCallback(), {
25944                 form:this.form.el.dom,
25945                 url:this.getUrl(!isPost),
25946                 method: method,
25947                 params:isPost ? this.getParams() : null,
25948                 isUpload: this.form.fileUpload,
25949                 formData : this.form.formData
25950             }));
25951             
25952             this.uploadProgress();
25953
25954         }else if (o.clientValidation !== false){ // client validation failed
25955             this.failureType = Roo.form.Action.CLIENT_INVALID;
25956             this.form.afterAction(this, false);
25957         }
25958     },
25959
25960     success : function(response)
25961     {
25962         this.uploadComplete= true;
25963         if (this.haveProgress) {
25964             Roo.MessageBox.hide();
25965         }
25966         
25967         
25968         var result = this.processResponse(response);
25969         if(result === true || result.success){
25970             this.form.afterAction(this, true);
25971             return;
25972         }
25973         if(result.errors){
25974             this.form.markInvalid(result.errors);
25975             this.failureType = Roo.form.Action.SERVER_INVALID;
25976         }
25977         this.form.afterAction(this, false);
25978     },
25979     failure : function(response)
25980     {
25981         this.uploadComplete= true;
25982         if (this.haveProgress) {
25983             Roo.MessageBox.hide();
25984         }
25985         
25986         this.response = response;
25987         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25988         this.form.afterAction(this, false);
25989     },
25990     
25991     handleResponse : function(response){
25992         if(this.form.errorReader){
25993             var rs = this.form.errorReader.read(response);
25994             var errors = [];
25995             if(rs.records){
25996                 for(var i = 0, len = rs.records.length; i < len; i++) {
25997                     var r = rs.records[i];
25998                     errors[i] = r.data;
25999                 }
26000             }
26001             if(errors.length < 1){
26002                 errors = null;
26003             }
26004             return {
26005                 success : rs.success,
26006                 errors : errors
26007             };
26008         }
26009         var ret = false;
26010         try {
26011             ret = Roo.decode(response.responseText);
26012         } catch (e) {
26013             ret = {
26014                 success: false,
26015                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26016                 errors : []
26017             };
26018         }
26019         return ret;
26020         
26021     }
26022 });
26023
26024
26025 Roo.form.Action.Load = function(form, options){
26026     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26027     this.reader = this.form.reader;
26028 };
26029
26030 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26031     type : 'load',
26032
26033     run : function(){
26034         
26035         Roo.Ajax.request(Roo.apply(
26036                 this.createCallback(), {
26037                     method:this.getMethod(),
26038                     url:this.getUrl(false),
26039                     params:this.getParams()
26040         }));
26041     },
26042
26043     success : function(response){
26044         
26045         var result = this.processResponse(response);
26046         if(result === true || !result.success || !result.data){
26047             this.failureType = Roo.form.Action.LOAD_FAILURE;
26048             this.form.afterAction(this, false);
26049             return;
26050         }
26051         this.form.clearInvalid();
26052         this.form.setValues(result.data);
26053         this.form.afterAction(this, true);
26054     },
26055
26056     handleResponse : function(response){
26057         if(this.form.reader){
26058             var rs = this.form.reader.read(response);
26059             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26060             return {
26061                 success : rs.success,
26062                 data : data
26063             };
26064         }
26065         return Roo.decode(response.responseText);
26066     }
26067 });
26068
26069 Roo.form.Action.ACTION_TYPES = {
26070     'load' : Roo.form.Action.Load,
26071     'submit' : Roo.form.Action.Submit
26072 };/*
26073  * Based on:
26074  * Ext JS Library 1.1.1
26075  * Copyright(c) 2006-2007, Ext JS, LLC.
26076  *
26077  * Originally Released Under LGPL - original licence link has changed is not relivant.
26078  *
26079  * Fork - LGPL
26080  * <script type="text/javascript">
26081  */
26082  
26083 /**
26084  * @class Roo.form.Layout
26085  * @extends Roo.Component
26086  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26087  * @constructor
26088  * @param {Object} config Configuration options
26089  */
26090 Roo.form.Layout = function(config){
26091     var xitems = [];
26092     if (config.items) {
26093         xitems = config.items;
26094         delete config.items;
26095     }
26096     Roo.form.Layout.superclass.constructor.call(this, config);
26097     this.stack = [];
26098     Roo.each(xitems, this.addxtype, this);
26099      
26100 };
26101
26102 Roo.extend(Roo.form.Layout, Roo.Component, {
26103     /**
26104      * @cfg {String/Object} autoCreate
26105      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26106      */
26107     /**
26108      * @cfg {String/Object/Function} style
26109      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26110      * a function which returns such a specification.
26111      */
26112     /**
26113      * @cfg {String} labelAlign
26114      * Valid values are "left," "top" and "right" (defaults to "left")
26115      */
26116     /**
26117      * @cfg {Number} labelWidth
26118      * Fixed width in pixels of all field labels (defaults to undefined)
26119      */
26120     /**
26121      * @cfg {Boolean} clear
26122      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26123      */
26124     clear : true,
26125     /**
26126      * @cfg {String} labelSeparator
26127      * The separator to use after field labels (defaults to ':')
26128      */
26129     labelSeparator : ':',
26130     /**
26131      * @cfg {Boolean} hideLabels
26132      * True to suppress the display of field labels in this layout (defaults to false)
26133      */
26134     hideLabels : false,
26135
26136     // private
26137     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26138     
26139     isLayout : true,
26140     
26141     // private
26142     onRender : function(ct, position){
26143         if(this.el){ // from markup
26144             this.el = Roo.get(this.el);
26145         }else {  // generate
26146             var cfg = this.getAutoCreate();
26147             this.el = ct.createChild(cfg, position);
26148         }
26149         if(this.style){
26150             this.el.applyStyles(this.style);
26151         }
26152         if(this.labelAlign){
26153             this.el.addClass('x-form-label-'+this.labelAlign);
26154         }
26155         if(this.hideLabels){
26156             this.labelStyle = "display:none";
26157             this.elementStyle = "padding-left:0;";
26158         }else{
26159             if(typeof this.labelWidth == 'number'){
26160                 this.labelStyle = "width:"+this.labelWidth+"px;";
26161                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26162             }
26163             if(this.labelAlign == 'top'){
26164                 this.labelStyle = "width:auto;";
26165                 this.elementStyle = "padding-left:0;";
26166             }
26167         }
26168         var stack = this.stack;
26169         var slen = stack.length;
26170         if(slen > 0){
26171             if(!this.fieldTpl){
26172                 var t = new Roo.Template(
26173                     '<div class="x-form-item {5}">',
26174                         '<label for="{0}" style="{2}">{1}{4}</label>',
26175                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26176                         '</div>',
26177                     '</div><div class="x-form-clear-left"></div>'
26178                 );
26179                 t.disableFormats = true;
26180                 t.compile();
26181                 Roo.form.Layout.prototype.fieldTpl = t;
26182             }
26183             for(var i = 0; i < slen; i++) {
26184                 if(stack[i].isFormField){
26185                     this.renderField(stack[i]);
26186                 }else{
26187                     this.renderComponent(stack[i]);
26188                 }
26189             }
26190         }
26191         if(this.clear){
26192             this.el.createChild({cls:'x-form-clear'});
26193         }
26194     },
26195
26196     // private
26197     renderField : function(f){
26198         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26199                f.id, //0
26200                f.fieldLabel, //1
26201                f.labelStyle||this.labelStyle||'', //2
26202                this.elementStyle||'', //3
26203                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26204                f.itemCls||this.itemCls||''  //5
26205        ], true).getPrevSibling());
26206     },
26207
26208     // private
26209     renderComponent : function(c){
26210         c.render(c.isLayout ? this.el : this.el.createChild());    
26211     },
26212     /**
26213      * Adds a object form elements (using the xtype property as the factory method.)
26214      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26215      * @param {Object} config 
26216      */
26217     addxtype : function(o)
26218     {
26219         // create the lement.
26220         o.form = this.form;
26221         var fe = Roo.factory(o, Roo.form);
26222         this.form.allItems.push(fe);
26223         this.stack.push(fe);
26224         
26225         if (fe.isFormField) {
26226             this.form.items.add(fe);
26227         }
26228          
26229         return fe;
26230     }
26231 });
26232
26233 /**
26234  * @class Roo.form.Column
26235  * @extends Roo.form.Layout
26236  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26237  * @constructor
26238  * @param {Object} config Configuration options
26239  */
26240 Roo.form.Column = function(config){
26241     Roo.form.Column.superclass.constructor.call(this, config);
26242 };
26243
26244 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26245     /**
26246      * @cfg {Number/String} width
26247      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26248      */
26249     /**
26250      * @cfg {String/Object} autoCreate
26251      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26252      */
26253
26254     // private
26255     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26256
26257     // private
26258     onRender : function(ct, position){
26259         Roo.form.Column.superclass.onRender.call(this, ct, position);
26260         if(this.width){
26261             this.el.setWidth(this.width);
26262         }
26263     }
26264 });
26265
26266
26267 /**
26268  * @class Roo.form.Row
26269  * @extends Roo.form.Layout
26270  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26271  * @constructor
26272  * @param {Object} config Configuration options
26273  */
26274
26275  
26276 Roo.form.Row = function(config){
26277     Roo.form.Row.superclass.constructor.call(this, config);
26278 };
26279  
26280 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26281       /**
26282      * @cfg {Number/String} width
26283      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26284      */
26285     /**
26286      * @cfg {Number/String} height
26287      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26288      */
26289     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26290     
26291     padWidth : 20,
26292     // private
26293     onRender : function(ct, position){
26294         //console.log('row render');
26295         if(!this.rowTpl){
26296             var t = new Roo.Template(
26297                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26298                     '<label for="{0}" style="{2}">{1}{4}</label>',
26299                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26300                     '</div>',
26301                 '</div>'
26302             );
26303             t.disableFormats = true;
26304             t.compile();
26305             Roo.form.Layout.prototype.rowTpl = t;
26306         }
26307         this.fieldTpl = this.rowTpl;
26308         
26309         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26310         var labelWidth = 100;
26311         
26312         if ((this.labelAlign != 'top')) {
26313             if (typeof this.labelWidth == 'number') {
26314                 labelWidth = this.labelWidth
26315             }
26316             this.padWidth =  20 + labelWidth;
26317             
26318         }
26319         
26320         Roo.form.Column.superclass.onRender.call(this, ct, position);
26321         if(this.width){
26322             this.el.setWidth(this.width);
26323         }
26324         if(this.height){
26325             this.el.setHeight(this.height);
26326         }
26327     },
26328     
26329     // private
26330     renderField : function(f){
26331         f.fieldEl = this.fieldTpl.append(this.el, [
26332                f.id, f.fieldLabel,
26333                f.labelStyle||this.labelStyle||'',
26334                this.elementStyle||'',
26335                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26336                f.itemCls||this.itemCls||'',
26337                f.width ? f.width + this.padWidth : 160 + this.padWidth
26338        ],true);
26339     }
26340 });
26341  
26342
26343 /**
26344  * @class Roo.form.FieldSet
26345  * @extends Roo.form.Layout
26346  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26347  * @constructor
26348  * @param {Object} config Configuration options
26349  */
26350 Roo.form.FieldSet = function(config){
26351     Roo.form.FieldSet.superclass.constructor.call(this, config);
26352 };
26353
26354 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26355     /**
26356      * @cfg {String} legend
26357      * The text to display as the legend for the FieldSet (defaults to '')
26358      */
26359     /**
26360      * @cfg {String/Object} autoCreate
26361      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26362      */
26363
26364     // private
26365     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26366
26367     // private
26368     onRender : function(ct, position){
26369         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26370         if(this.legend){
26371             this.setLegend(this.legend);
26372         }
26373     },
26374
26375     // private
26376     setLegend : function(text){
26377         if(this.rendered){
26378             this.el.child('legend').update(text);
26379         }
26380     }
26381 });/*
26382  * Based on:
26383  * Ext JS Library 1.1.1
26384  * Copyright(c) 2006-2007, Ext JS, LLC.
26385  *
26386  * Originally Released Under LGPL - original licence link has changed is not relivant.
26387  *
26388  * Fork - LGPL
26389  * <script type="text/javascript">
26390  */
26391 /**
26392  * @class Roo.form.VTypes
26393  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26394  * @singleton
26395  */
26396 Roo.form.VTypes = function(){
26397     // closure these in so they are only created once.
26398     var alpha = /^[a-zA-Z_]+$/;
26399     var alphanum = /^[a-zA-Z0-9_]+$/;
26400     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26401     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26402
26403     // All these messages and functions are configurable
26404     return {
26405         /**
26406          * The function used to validate email addresses
26407          * @param {String} value The email address
26408          */
26409         'email' : function(v){
26410             return email.test(v);
26411         },
26412         /**
26413          * The error text to display when the email validation function returns false
26414          * @type String
26415          */
26416         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26417         /**
26418          * The keystroke filter mask to be applied on email input
26419          * @type RegExp
26420          */
26421         'emailMask' : /[a-z0-9_\.\-@]/i,
26422
26423         /**
26424          * The function used to validate URLs
26425          * @param {String} value The URL
26426          */
26427         'url' : function(v){
26428             return url.test(v);
26429         },
26430         /**
26431          * The error text to display when the url validation function returns false
26432          * @type String
26433          */
26434         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26435         
26436         /**
26437          * The function used to validate alpha values
26438          * @param {String} value The value
26439          */
26440         'alpha' : function(v){
26441             return alpha.test(v);
26442         },
26443         /**
26444          * The error text to display when the alpha validation function returns false
26445          * @type String
26446          */
26447         'alphaText' : 'This field should only contain letters and _',
26448         /**
26449          * The keystroke filter mask to be applied on alpha input
26450          * @type RegExp
26451          */
26452         'alphaMask' : /[a-z_]/i,
26453
26454         /**
26455          * The function used to validate alphanumeric values
26456          * @param {String} value The value
26457          */
26458         'alphanum' : function(v){
26459             return alphanum.test(v);
26460         },
26461         /**
26462          * The error text to display when the alphanumeric validation function returns false
26463          * @type String
26464          */
26465         'alphanumText' : 'This field should only contain letters, numbers and _',
26466         /**
26467          * The keystroke filter mask to be applied on alphanumeric input
26468          * @type RegExp
26469          */
26470         'alphanumMask' : /[a-z0-9_]/i
26471     };
26472 }();//<script type="text/javascript">
26473
26474 /**
26475  * @class Roo.form.FCKeditor
26476  * @extends Roo.form.TextArea
26477  * Wrapper around the FCKEditor http://www.fckeditor.net
26478  * @constructor
26479  * Creates a new FCKeditor
26480  * @param {Object} config Configuration options
26481  */
26482 Roo.form.FCKeditor = function(config){
26483     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26484     this.addEvents({
26485          /**
26486          * @event editorinit
26487          * Fired when the editor is initialized - you can add extra handlers here..
26488          * @param {FCKeditor} this
26489          * @param {Object} the FCK object.
26490          */
26491         editorinit : true
26492     });
26493     
26494     
26495 };
26496 Roo.form.FCKeditor.editors = { };
26497 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26498 {
26499     //defaultAutoCreate : {
26500     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26501     //},
26502     // private
26503     /**
26504      * @cfg {Object} fck options - see fck manual for details.
26505      */
26506     fckconfig : false,
26507     
26508     /**
26509      * @cfg {Object} fck toolbar set (Basic or Default)
26510      */
26511     toolbarSet : 'Basic',
26512     /**
26513      * @cfg {Object} fck BasePath
26514      */ 
26515     basePath : '/fckeditor/',
26516     
26517     
26518     frame : false,
26519     
26520     value : '',
26521     
26522    
26523     onRender : function(ct, position)
26524     {
26525         if(!this.el){
26526             this.defaultAutoCreate = {
26527                 tag: "textarea",
26528                 style:"width:300px;height:60px;",
26529                 autocomplete: "new-password"
26530             };
26531         }
26532         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26533         /*
26534         if(this.grow){
26535             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26536             if(this.preventScrollbars){
26537                 this.el.setStyle("overflow", "hidden");
26538             }
26539             this.el.setHeight(this.growMin);
26540         }
26541         */
26542         //console.log('onrender' + this.getId() );
26543         Roo.form.FCKeditor.editors[this.getId()] = this;
26544          
26545
26546         this.replaceTextarea() ;
26547         
26548     },
26549     
26550     getEditor : function() {
26551         return this.fckEditor;
26552     },
26553     /**
26554      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26555      * @param {Mixed} value The value to set
26556      */
26557     
26558     
26559     setValue : function(value)
26560     {
26561         //console.log('setValue: ' + value);
26562         
26563         if(typeof(value) == 'undefined') { // not sure why this is happending...
26564             return;
26565         }
26566         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26567         
26568         //if(!this.el || !this.getEditor()) {
26569         //    this.value = value;
26570             //this.setValue.defer(100,this,[value]);    
26571         //    return;
26572         //} 
26573         
26574         if(!this.getEditor()) {
26575             return;
26576         }
26577         
26578         this.getEditor().SetData(value);
26579         
26580         //
26581
26582     },
26583
26584     /**
26585      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26586      * @return {Mixed} value The field value
26587      */
26588     getValue : function()
26589     {
26590         
26591         if (this.frame && this.frame.dom.style.display == 'none') {
26592             return Roo.form.FCKeditor.superclass.getValue.call(this);
26593         }
26594         
26595         if(!this.el || !this.getEditor()) {
26596            
26597            // this.getValue.defer(100,this); 
26598             return this.value;
26599         }
26600        
26601         
26602         var value=this.getEditor().GetData();
26603         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26604         return Roo.form.FCKeditor.superclass.getValue.call(this);
26605         
26606
26607     },
26608
26609     /**
26610      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26611      * @return {Mixed} value The field value
26612      */
26613     getRawValue : function()
26614     {
26615         if (this.frame && this.frame.dom.style.display == 'none') {
26616             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26617         }
26618         
26619         if(!this.el || !this.getEditor()) {
26620             //this.getRawValue.defer(100,this); 
26621             return this.value;
26622             return;
26623         }
26624         
26625         
26626         
26627         var value=this.getEditor().GetData();
26628         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26629         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26630          
26631     },
26632     
26633     setSize : function(w,h) {
26634         
26635         
26636         
26637         //if (this.frame && this.frame.dom.style.display == 'none') {
26638         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26639         //    return;
26640         //}
26641         //if(!this.el || !this.getEditor()) {
26642         //    this.setSize.defer(100,this, [w,h]); 
26643         //    return;
26644         //}
26645         
26646         
26647         
26648         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26649         
26650         this.frame.dom.setAttribute('width', w);
26651         this.frame.dom.setAttribute('height', h);
26652         this.frame.setSize(w,h);
26653         
26654     },
26655     
26656     toggleSourceEdit : function(value) {
26657         
26658       
26659          
26660         this.el.dom.style.display = value ? '' : 'none';
26661         this.frame.dom.style.display = value ?  'none' : '';
26662         
26663     },
26664     
26665     
26666     focus: function(tag)
26667     {
26668         if (this.frame.dom.style.display == 'none') {
26669             return Roo.form.FCKeditor.superclass.focus.call(this);
26670         }
26671         if(!this.el || !this.getEditor()) {
26672             this.focus.defer(100,this, [tag]); 
26673             return;
26674         }
26675         
26676         
26677         
26678         
26679         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26680         this.getEditor().Focus();
26681         if (tgs.length) {
26682             if (!this.getEditor().Selection.GetSelection()) {
26683                 this.focus.defer(100,this, [tag]); 
26684                 return;
26685             }
26686             
26687             
26688             var r = this.getEditor().EditorDocument.createRange();
26689             r.setStart(tgs[0],0);
26690             r.setEnd(tgs[0],0);
26691             this.getEditor().Selection.GetSelection().removeAllRanges();
26692             this.getEditor().Selection.GetSelection().addRange(r);
26693             this.getEditor().Focus();
26694         }
26695         
26696     },
26697     
26698     
26699     
26700     replaceTextarea : function()
26701     {
26702         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26703             return ;
26704         }
26705         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26706         //{
26707             // We must check the elements firstly using the Id and then the name.
26708         var oTextarea = document.getElementById( this.getId() );
26709         
26710         var colElementsByName = document.getElementsByName( this.getId() ) ;
26711          
26712         oTextarea.style.display = 'none' ;
26713
26714         if ( oTextarea.tabIndex ) {            
26715             this.TabIndex = oTextarea.tabIndex ;
26716         }
26717         
26718         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26719         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26720         this.frame = Roo.get(this.getId() + '___Frame')
26721     },
26722     
26723     _getConfigHtml : function()
26724     {
26725         var sConfig = '' ;
26726
26727         for ( var o in this.fckconfig ) {
26728             sConfig += sConfig.length > 0  ? '&amp;' : '';
26729             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26730         }
26731
26732         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26733     },
26734     
26735     
26736     _getIFrameHtml : function()
26737     {
26738         var sFile = 'fckeditor.html' ;
26739         /* no idea what this is about..
26740         try
26741         {
26742             if ( (/fcksource=true/i).test( window.top.location.search ) )
26743                 sFile = 'fckeditor.original.html' ;
26744         }
26745         catch (e) { 
26746         */
26747
26748         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26749         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26750         
26751         
26752         var html = '<iframe id="' + this.getId() +
26753             '___Frame" src="' + sLink +
26754             '" width="' + this.width +
26755             '" height="' + this.height + '"' +
26756             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26757             ' frameborder="0" scrolling="no"></iframe>' ;
26758
26759         return html ;
26760     },
26761     
26762     _insertHtmlBefore : function( html, element )
26763     {
26764         if ( element.insertAdjacentHTML )       {
26765             // IE
26766             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26767         } else { // Gecko
26768             var oRange = document.createRange() ;
26769             oRange.setStartBefore( element ) ;
26770             var oFragment = oRange.createContextualFragment( html );
26771             element.parentNode.insertBefore( oFragment, element ) ;
26772         }
26773     }
26774     
26775     
26776   
26777     
26778     
26779     
26780     
26781
26782 });
26783
26784 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26785
26786 function FCKeditor_OnComplete(editorInstance){
26787     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26788     f.fckEditor = editorInstance;
26789     //console.log("loaded");
26790     f.fireEvent('editorinit', f, editorInstance);
26791
26792   
26793
26794  
26795
26796
26797
26798
26799
26800
26801
26802
26803
26804
26805
26806
26807
26808
26809
26810 //<script type="text/javascript">
26811 /**
26812  * @class Roo.form.GridField
26813  * @extends Roo.form.Field
26814  * Embed a grid (or editable grid into a form)
26815  * STATUS ALPHA
26816  * 
26817  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26818  * it needs 
26819  * xgrid.store = Roo.data.Store
26820  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26821  * xgrid.store.reader = Roo.data.JsonReader 
26822  * 
26823  * 
26824  * @constructor
26825  * Creates a new GridField
26826  * @param {Object} config Configuration options
26827  */
26828 Roo.form.GridField = function(config){
26829     Roo.form.GridField.superclass.constructor.call(this, config);
26830      
26831 };
26832
26833 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26834     /**
26835      * @cfg {Number} width  - used to restrict width of grid..
26836      */
26837     width : 100,
26838     /**
26839      * @cfg {Number} height - used to restrict height of grid..
26840      */
26841     height : 50,
26842      /**
26843      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26844          * 
26845          *}
26846      */
26847     xgrid : false, 
26848     /**
26849      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26850      * {tag: "input", type: "checkbox", autocomplete: "off"})
26851      */
26852    // defaultAutoCreate : { tag: 'div' },
26853     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26854     /**
26855      * @cfg {String} addTitle Text to include for adding a title.
26856      */
26857     addTitle : false,
26858     //
26859     onResize : function(){
26860         Roo.form.Field.superclass.onResize.apply(this, arguments);
26861     },
26862
26863     initEvents : function(){
26864         // Roo.form.Checkbox.superclass.initEvents.call(this);
26865         // has no events...
26866        
26867     },
26868
26869
26870     getResizeEl : function(){
26871         return this.wrap;
26872     },
26873
26874     getPositionEl : function(){
26875         return this.wrap;
26876     },
26877
26878     // private
26879     onRender : function(ct, position){
26880         
26881         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26882         var style = this.style;
26883         delete this.style;
26884         
26885         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26886         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26887         this.viewEl = this.wrap.createChild({ tag: 'div' });
26888         if (style) {
26889             this.viewEl.applyStyles(style);
26890         }
26891         if (this.width) {
26892             this.viewEl.setWidth(this.width);
26893         }
26894         if (this.height) {
26895             this.viewEl.setHeight(this.height);
26896         }
26897         //if(this.inputValue !== undefined){
26898         //this.setValue(this.value);
26899         
26900         
26901         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26902         
26903         
26904         this.grid.render();
26905         this.grid.getDataSource().on('remove', this.refreshValue, this);
26906         this.grid.getDataSource().on('update', this.refreshValue, this);
26907         this.grid.on('afteredit', this.refreshValue, this);
26908  
26909     },
26910      
26911     
26912     /**
26913      * Sets the value of the item. 
26914      * @param {String} either an object  or a string..
26915      */
26916     setValue : function(v){
26917         //this.value = v;
26918         v = v || []; // empty set..
26919         // this does not seem smart - it really only affects memoryproxy grids..
26920         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26921             var ds = this.grid.getDataSource();
26922             // assumes a json reader..
26923             var data = {}
26924             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
26925             ds.loadData( data);
26926         }
26927         // clear selection so it does not get stale.
26928         if (this.grid.sm) { 
26929             this.grid.sm.clearSelections();
26930         }
26931         
26932         Roo.form.GridField.superclass.setValue.call(this, v);
26933         this.refreshValue();
26934         // should load data in the grid really....
26935     },
26936     
26937     // private
26938     refreshValue: function() {
26939          var val = [];
26940         this.grid.getDataSource().each(function(r) {
26941             val.push(r.data);
26942         });
26943         this.el.dom.value = Roo.encode(val);
26944     }
26945     
26946      
26947     
26948     
26949 });/*
26950  * Based on:
26951  * Ext JS Library 1.1.1
26952  * Copyright(c) 2006-2007, Ext JS, LLC.
26953  *
26954  * Originally Released Under LGPL - original licence link has changed is not relivant.
26955  *
26956  * Fork - LGPL
26957  * <script type="text/javascript">
26958  */
26959 /**
26960  * @class Roo.form.DisplayField
26961  * @extends Roo.form.Field
26962  * A generic Field to display non-editable data.
26963  * @cfg {Boolean} closable (true|false) default false
26964  * @constructor
26965  * Creates a new Display Field item.
26966  * @param {Object} config Configuration options
26967  */
26968 Roo.form.DisplayField = function(config){
26969     Roo.form.DisplayField.superclass.constructor.call(this, config);
26970     
26971     this.addEvents({
26972         /**
26973          * @event close
26974          * Fires after the click the close btn
26975              * @param {Roo.form.DisplayField} this
26976              */
26977         close : true
26978     });
26979 };
26980
26981 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
26982     inputType:      'hidden',
26983     allowBlank:     true,
26984     readOnly:         true,
26985     
26986  
26987     /**
26988      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26989      */
26990     focusClass : undefined,
26991     /**
26992      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26993      */
26994     fieldClass: 'x-form-field',
26995     
26996      /**
26997      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
26998      */
26999     valueRenderer: undefined,
27000     
27001     width: 100,
27002     /**
27003      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27004      * {tag: "input", type: "checkbox", autocomplete: "off"})
27005      */
27006      
27007  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27008  
27009     closable : false,
27010     
27011     onResize : function(){
27012         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27013         
27014     },
27015
27016     initEvents : function(){
27017         // Roo.form.Checkbox.superclass.initEvents.call(this);
27018         // has no events...
27019         
27020         if(this.closable){
27021             this.closeEl.on('click', this.onClose, this);
27022         }
27023        
27024     },
27025
27026
27027     getResizeEl : function(){
27028         return this.wrap;
27029     },
27030
27031     getPositionEl : function(){
27032         return this.wrap;
27033     },
27034
27035     // private
27036     onRender : function(ct, position){
27037         
27038         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27039         //if(this.inputValue !== undefined){
27040         this.wrap = this.el.wrap();
27041         
27042         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27043         
27044         if(this.closable){
27045             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27046         }
27047         
27048         if (this.bodyStyle) {
27049             this.viewEl.applyStyles(this.bodyStyle);
27050         }
27051         //this.viewEl.setStyle('padding', '2px');
27052         
27053         this.setValue(this.value);
27054         
27055     },
27056 /*
27057     // private
27058     initValue : Roo.emptyFn,
27059
27060   */
27061
27062         // private
27063     onClick : function(){
27064         
27065     },
27066
27067     /**
27068      * Sets the checked state of the checkbox.
27069      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27070      */
27071     setValue : function(v){
27072         this.value = v;
27073         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27074         // this might be called before we have a dom element..
27075         if (!this.viewEl) {
27076             return;
27077         }
27078         this.viewEl.dom.innerHTML = html;
27079         Roo.form.DisplayField.superclass.setValue.call(this, v);
27080
27081     },
27082     
27083     onClose : function(e)
27084     {
27085         e.preventDefault();
27086         
27087         this.fireEvent('close', this);
27088     }
27089 });/*
27090  * 
27091  * Licence- LGPL
27092  * 
27093  */
27094
27095 /**
27096  * @class Roo.form.DayPicker
27097  * @extends Roo.form.Field
27098  * A Day picker show [M] [T] [W] ....
27099  * @constructor
27100  * Creates a new Day Picker
27101  * @param {Object} config Configuration options
27102  */
27103 Roo.form.DayPicker= function(config){
27104     Roo.form.DayPicker.superclass.constructor.call(this, config);
27105      
27106 };
27107
27108 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27109     /**
27110      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27111      */
27112     focusClass : undefined,
27113     /**
27114      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27115      */
27116     fieldClass: "x-form-field",
27117    
27118     /**
27119      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27120      * {tag: "input", type: "checkbox", autocomplete: "off"})
27121      */
27122     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27123     
27124    
27125     actionMode : 'viewEl', 
27126     //
27127     // private
27128  
27129     inputType : 'hidden',
27130     
27131      
27132     inputElement: false, // real input element?
27133     basedOn: false, // ????
27134     
27135     isFormField: true, // not sure where this is needed!!!!
27136
27137     onResize : function(){
27138         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27139         if(!this.boxLabel){
27140             this.el.alignTo(this.wrap, 'c-c');
27141         }
27142     },
27143
27144     initEvents : function(){
27145         Roo.form.Checkbox.superclass.initEvents.call(this);
27146         this.el.on("click", this.onClick,  this);
27147         this.el.on("change", this.onClick,  this);
27148     },
27149
27150
27151     getResizeEl : function(){
27152         return this.wrap;
27153     },
27154
27155     getPositionEl : function(){
27156         return this.wrap;
27157     },
27158
27159     
27160     // private
27161     onRender : function(ct, position){
27162         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27163        
27164         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27165         
27166         var r1 = '<table><tr>';
27167         var r2 = '<tr class="x-form-daypick-icons">';
27168         for (var i=0; i < 7; i++) {
27169             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27170             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27171         }
27172         
27173         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27174         viewEl.select('img').on('click', this.onClick, this);
27175         this.viewEl = viewEl;   
27176         
27177         
27178         // this will not work on Chrome!!!
27179         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27180         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27181         
27182         
27183           
27184
27185     },
27186
27187     // private
27188     initValue : Roo.emptyFn,
27189
27190     /**
27191      * Returns the checked state of the checkbox.
27192      * @return {Boolean} True if checked, else false
27193      */
27194     getValue : function(){
27195         return this.el.dom.value;
27196         
27197     },
27198
27199         // private
27200     onClick : function(e){ 
27201         //this.setChecked(!this.checked);
27202         Roo.get(e.target).toggleClass('x-menu-item-checked');
27203         this.refreshValue();
27204         //if(this.el.dom.checked != this.checked){
27205         //    this.setValue(this.el.dom.checked);
27206        // }
27207     },
27208     
27209     // private
27210     refreshValue : function()
27211     {
27212         var val = '';
27213         this.viewEl.select('img',true).each(function(e,i,n)  {
27214             val += e.is(".x-menu-item-checked") ? String(n) : '';
27215         });
27216         this.setValue(val, true);
27217     },
27218
27219     /**
27220      * Sets the checked state of the checkbox.
27221      * On is always based on a string comparison between inputValue and the param.
27222      * @param {Boolean/String} value - the value to set 
27223      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27224      */
27225     setValue : function(v,suppressEvent){
27226         if (!this.el.dom) {
27227             return;
27228         }
27229         var old = this.el.dom.value ;
27230         this.el.dom.value = v;
27231         if (suppressEvent) {
27232             return ;
27233         }
27234          
27235         // update display..
27236         this.viewEl.select('img',true).each(function(e,i,n)  {
27237             
27238             var on = e.is(".x-menu-item-checked");
27239             var newv = v.indexOf(String(n)) > -1;
27240             if (on != newv) {
27241                 e.toggleClass('x-menu-item-checked');
27242             }
27243             
27244         });
27245         
27246         
27247         this.fireEvent('change', this, v, old);
27248         
27249         
27250     },
27251    
27252     // handle setting of hidden value by some other method!!?!?
27253     setFromHidden: function()
27254     {
27255         if(!this.el){
27256             return;
27257         }
27258         //console.log("SET FROM HIDDEN");
27259         //alert('setFrom hidden');
27260         this.setValue(this.el.dom.value);
27261     },
27262     
27263     onDestroy : function()
27264     {
27265         if(this.viewEl){
27266             Roo.get(this.viewEl).remove();
27267         }
27268          
27269         Roo.form.DayPicker.superclass.onDestroy.call(this);
27270     }
27271
27272 });/*
27273  * RooJS Library 1.1.1
27274  * Copyright(c) 2008-2011  Alan Knowles
27275  *
27276  * License - LGPL
27277  */
27278  
27279
27280 /**
27281  * @class Roo.form.ComboCheck
27282  * @extends Roo.form.ComboBox
27283  * A combobox for multiple select items.
27284  *
27285  * FIXME - could do with a reset button..
27286  * 
27287  * @constructor
27288  * Create a new ComboCheck
27289  * @param {Object} config Configuration options
27290  */
27291 Roo.form.ComboCheck = function(config){
27292     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27293     // should verify some data...
27294     // like
27295     // hiddenName = required..
27296     // displayField = required
27297     // valudField == required
27298     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27299     var _t = this;
27300     Roo.each(req, function(e) {
27301         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27302             throw "Roo.form.ComboCheck : missing value for: " + e;
27303         }
27304     });
27305     
27306     
27307 };
27308
27309 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27310      
27311      
27312     editable : false,
27313      
27314     selectedClass: 'x-menu-item-checked', 
27315     
27316     // private
27317     onRender : function(ct, position){
27318         var _t = this;
27319         
27320         
27321         
27322         if(!this.tpl){
27323             var cls = 'x-combo-list';
27324
27325             
27326             this.tpl =  new Roo.Template({
27327                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27328                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27329                    '<span>{' + this.displayField + '}</span>' +
27330                     '</div>' 
27331                 
27332             });
27333         }
27334  
27335         
27336         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27337         this.view.singleSelect = false;
27338         this.view.multiSelect = true;
27339         this.view.toggleSelect = true;
27340         this.pageTb.add(new Roo.Toolbar.Fill(), {
27341             
27342             text: 'Done',
27343             handler: function()
27344             {
27345                 _t.collapse();
27346             }
27347         });
27348     },
27349     
27350     onViewOver : function(e, t){
27351         // do nothing...
27352         return;
27353         
27354     },
27355     
27356     onViewClick : function(doFocus,index){
27357         return;
27358         
27359     },
27360     select: function () {
27361         //Roo.log("SELECT CALLED");
27362     },
27363      
27364     selectByValue : function(xv, scrollIntoView){
27365         var ar = this.getValueArray();
27366         var sels = [];
27367         
27368         Roo.each(ar, function(v) {
27369             if(v === undefined || v === null){
27370                 return;
27371             }
27372             var r = this.findRecord(this.valueField, v);
27373             if(r){
27374                 sels.push(this.store.indexOf(r))
27375                 
27376             }
27377         },this);
27378         this.view.select(sels);
27379         return false;
27380     },
27381     
27382     
27383     
27384     onSelect : function(record, index){
27385        // Roo.log("onselect Called");
27386        // this is only called by the clear button now..
27387         this.view.clearSelections();
27388         this.setValue('[]');
27389         if (this.value != this.valueBefore) {
27390             this.fireEvent('change', this, this.value, this.valueBefore);
27391             this.valueBefore = this.value;
27392         }
27393     },
27394     getValueArray : function()
27395     {
27396         var ar = [] ;
27397         
27398         try {
27399             //Roo.log(this.value);
27400             if (typeof(this.value) == 'undefined') {
27401                 return [];
27402             }
27403             var ar = Roo.decode(this.value);
27404             return  ar instanceof Array ? ar : []; //?? valid?
27405             
27406         } catch(e) {
27407             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27408             return [];
27409         }
27410          
27411     },
27412     expand : function ()
27413     {
27414         
27415         Roo.form.ComboCheck.superclass.expand.call(this);
27416         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27417         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27418         
27419
27420     },
27421     
27422     collapse : function(){
27423         Roo.form.ComboCheck.superclass.collapse.call(this);
27424         var sl = this.view.getSelectedIndexes();
27425         var st = this.store;
27426         var nv = [];
27427         var tv = [];
27428         var r;
27429         Roo.each(sl, function(i) {
27430             r = st.getAt(i);
27431             nv.push(r.get(this.valueField));
27432         },this);
27433         this.setValue(Roo.encode(nv));
27434         if (this.value != this.valueBefore) {
27435
27436             this.fireEvent('change', this, this.value, this.valueBefore);
27437             this.valueBefore = this.value;
27438         }
27439         
27440     },
27441     
27442     setValue : function(v){
27443         // Roo.log(v);
27444         this.value = v;
27445         
27446         var vals = this.getValueArray();
27447         var tv = [];
27448         Roo.each(vals, function(k) {
27449             var r = this.findRecord(this.valueField, k);
27450             if(r){
27451                 tv.push(r.data[this.displayField]);
27452             }else if(this.valueNotFoundText !== undefined){
27453                 tv.push( this.valueNotFoundText );
27454             }
27455         },this);
27456        // Roo.log(tv);
27457         
27458         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27459         this.hiddenField.value = v;
27460         this.value = v;
27461     }
27462     
27463 });/*
27464  * Based on:
27465  * Ext JS Library 1.1.1
27466  * Copyright(c) 2006-2007, Ext JS, LLC.
27467  *
27468  * Originally Released Under LGPL - original licence link has changed is not relivant.
27469  *
27470  * Fork - LGPL
27471  * <script type="text/javascript">
27472  */
27473  
27474 /**
27475  * @class Roo.form.Signature
27476  * @extends Roo.form.Field
27477  * Signature field.  
27478  * @constructor
27479  * 
27480  * @param {Object} config Configuration options
27481  */
27482
27483 Roo.form.Signature = function(config){
27484     Roo.form.Signature.superclass.constructor.call(this, config);
27485     
27486     this.addEvents({// not in used??
27487          /**
27488          * @event confirm
27489          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27490              * @param {Roo.form.Signature} combo This combo box
27491              */
27492         'confirm' : true,
27493         /**
27494          * @event reset
27495          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27496              * @param {Roo.form.ComboBox} combo This combo box
27497              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27498              */
27499         'reset' : true
27500     });
27501 };
27502
27503 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27504     /**
27505      * @cfg {Object} labels Label to use when rendering a form.
27506      * defaults to 
27507      * labels : { 
27508      *      clear : "Clear",
27509      *      confirm : "Confirm"
27510      *  }
27511      */
27512     labels : { 
27513         clear : "Clear",
27514         confirm : "Confirm"
27515     },
27516     /**
27517      * @cfg {Number} width The signature panel width (defaults to 300)
27518      */
27519     width: 300,
27520     /**
27521      * @cfg {Number} height The signature panel height (defaults to 100)
27522      */
27523     height : 100,
27524     /**
27525      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27526      */
27527     allowBlank : false,
27528     
27529     //private
27530     // {Object} signPanel The signature SVG panel element (defaults to {})
27531     signPanel : {},
27532     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27533     isMouseDown : false,
27534     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27535     isConfirmed : false,
27536     // {String} signatureTmp SVG mapping string (defaults to empty string)
27537     signatureTmp : '',
27538     
27539     
27540     defaultAutoCreate : { // modified by initCompnoent..
27541         tag: "input",
27542         type:"hidden"
27543     },
27544
27545     // private
27546     onRender : function(ct, position){
27547         
27548         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27549         
27550         this.wrap = this.el.wrap({
27551             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27552         });
27553         
27554         this.createToolbar(this);
27555         this.signPanel = this.wrap.createChild({
27556                 tag: 'div',
27557                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27558             }, this.el
27559         );
27560             
27561         this.svgID = Roo.id();
27562         this.svgEl = this.signPanel.createChild({
27563               xmlns : 'http://www.w3.org/2000/svg',
27564               tag : 'svg',
27565               id : this.svgID + "-svg",
27566               width: this.width,
27567               height: this.height,
27568               viewBox: '0 0 '+this.width+' '+this.height,
27569               cn : [
27570                 {
27571                     tag: "rect",
27572                     id: this.svgID + "-svg-r",
27573                     width: this.width,
27574                     height: this.height,
27575                     fill: "#ffa"
27576                 },
27577                 {
27578                     tag: "line",
27579                     id: this.svgID + "-svg-l",
27580                     x1: "0", // start
27581                     y1: (this.height*0.8), // start set the line in 80% of height
27582                     x2: this.width, // end
27583                     y2: (this.height*0.8), // end set the line in 80% of height
27584                     'stroke': "#666",
27585                     'stroke-width': "1",
27586                     'stroke-dasharray': "3",
27587                     'shape-rendering': "crispEdges",
27588                     'pointer-events': "none"
27589                 },
27590                 {
27591                     tag: "path",
27592                     id: this.svgID + "-svg-p",
27593                     'stroke': "navy",
27594                     'stroke-width': "3",
27595                     'fill': "none",
27596                     'pointer-events': 'none'
27597                 }
27598               ]
27599         });
27600         this.createSVG();
27601         this.svgBox = this.svgEl.dom.getScreenCTM();
27602     },
27603     createSVG : function(){ 
27604         var svg = this.signPanel;
27605         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27606         var t = this;
27607
27608         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27609         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27610         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27611         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27612         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27613         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27614         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27615         
27616     },
27617     isTouchEvent : function(e){
27618         return e.type.match(/^touch/);
27619     },
27620     getCoords : function (e) {
27621         var pt    = this.svgEl.dom.createSVGPoint();
27622         pt.x = e.clientX; 
27623         pt.y = e.clientY;
27624         if (this.isTouchEvent(e)) {
27625             pt.x =  e.targetTouches[0].clientX;
27626             pt.y = e.targetTouches[0].clientY;
27627         }
27628         var a = this.svgEl.dom.getScreenCTM();
27629         var b = a.inverse();
27630         var mx = pt.matrixTransform(b);
27631         return mx.x + ',' + mx.y;
27632     },
27633     //mouse event headler 
27634     down : function (e) {
27635         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27636         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27637         
27638         this.isMouseDown = true;
27639         
27640         e.preventDefault();
27641     },
27642     move : function (e) {
27643         if (this.isMouseDown) {
27644             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27645             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27646         }
27647         
27648         e.preventDefault();
27649     },
27650     up : function (e) {
27651         this.isMouseDown = false;
27652         var sp = this.signatureTmp.split(' ');
27653         
27654         if(sp.length > 1){
27655             if(!sp[sp.length-2].match(/^L/)){
27656                 sp.pop();
27657                 sp.pop();
27658                 sp.push("");
27659                 this.signatureTmp = sp.join(" ");
27660             }
27661         }
27662         if(this.getValue() != this.signatureTmp){
27663             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27664             this.isConfirmed = false;
27665         }
27666         e.preventDefault();
27667     },
27668     
27669     /**
27670      * Protected method that will not generally be called directly. It
27671      * is called when the editor creates its toolbar. Override this method if you need to
27672      * add custom toolbar buttons.
27673      * @param {HtmlEditor} editor
27674      */
27675     createToolbar : function(editor){
27676          function btn(id, toggle, handler){
27677             var xid = fid + '-'+ id ;
27678             return {
27679                 id : xid,
27680                 cmd : id,
27681                 cls : 'x-btn-icon x-edit-'+id,
27682                 enableToggle:toggle !== false,
27683                 scope: editor, // was editor...
27684                 handler:handler||editor.relayBtnCmd,
27685                 clickEvent:'mousedown',
27686                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27687                 tabIndex:-1
27688             };
27689         }
27690         
27691         
27692         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27693         this.tb = tb;
27694         this.tb.add(
27695            {
27696                 cls : ' x-signature-btn x-signature-'+id,
27697                 scope: editor, // was editor...
27698                 handler: this.reset,
27699                 clickEvent:'mousedown',
27700                 text: this.labels.clear
27701             },
27702             {
27703                  xtype : 'Fill',
27704                  xns: Roo.Toolbar
27705             }, 
27706             {
27707                 cls : '  x-signature-btn x-signature-'+id,
27708                 scope: editor, // was editor...
27709                 handler: this.confirmHandler,
27710                 clickEvent:'mousedown',
27711                 text: this.labels.confirm
27712             }
27713         );
27714     
27715     },
27716     //public
27717     /**
27718      * when user is clicked confirm then show this image.....
27719      * 
27720      * @return {String} Image Data URI
27721      */
27722     getImageDataURI : function(){
27723         var svg = this.svgEl.dom.parentNode.innerHTML;
27724         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27725         return src; 
27726     },
27727     /**
27728      * 
27729      * @return {Boolean} this.isConfirmed
27730      */
27731     getConfirmed : function(){
27732         return this.isConfirmed;
27733     },
27734     /**
27735      * 
27736      * @return {Number} this.width
27737      */
27738     getWidth : function(){
27739         return this.width;
27740     },
27741     /**
27742      * 
27743      * @return {Number} this.height
27744      */
27745     getHeight : function(){
27746         return this.height;
27747     },
27748     // private
27749     getSignature : function(){
27750         return this.signatureTmp;
27751     },
27752     // private
27753     reset : function(){
27754         this.signatureTmp = '';
27755         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27756         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27757         this.isConfirmed = false;
27758         Roo.form.Signature.superclass.reset.call(this);
27759     },
27760     setSignature : function(s){
27761         this.signatureTmp = s;
27762         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27763         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27764         this.setValue(s);
27765         this.isConfirmed = false;
27766         Roo.form.Signature.superclass.reset.call(this);
27767     }, 
27768     test : function(){
27769 //        Roo.log(this.signPanel.dom.contentWindow.up())
27770     },
27771     //private
27772     setConfirmed : function(){
27773         
27774         
27775         
27776 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27777     },
27778     // private
27779     confirmHandler : function(){
27780         if(!this.getSignature()){
27781             return;
27782         }
27783         
27784         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27785         this.setValue(this.getSignature());
27786         this.isConfirmed = true;
27787         
27788         this.fireEvent('confirm', this);
27789     },
27790     // private
27791     // Subclasses should provide the validation implementation by overriding this
27792     validateValue : function(value){
27793         if(this.allowBlank){
27794             return true;
27795         }
27796         
27797         if(this.isConfirmed){
27798             return true;
27799         }
27800         return false;
27801     }
27802 });/*
27803  * Based on:
27804  * Ext JS Library 1.1.1
27805  * Copyright(c) 2006-2007, Ext JS, LLC.
27806  *
27807  * Originally Released Under LGPL - original licence link has changed is not relivant.
27808  *
27809  * Fork - LGPL
27810  * <script type="text/javascript">
27811  */
27812  
27813
27814 /**
27815  * @class Roo.form.ComboBox
27816  * @extends Roo.form.TriggerField
27817  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27818  * @constructor
27819  * Create a new ComboBox.
27820  * @param {Object} config Configuration options
27821  */
27822 Roo.form.Select = function(config){
27823     Roo.form.Select.superclass.constructor.call(this, config);
27824      
27825 };
27826
27827 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27828     /**
27829      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27830      */
27831     /**
27832      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27833      * rendering into an Roo.Editor, defaults to false)
27834      */
27835     /**
27836      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27837      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27838      */
27839     /**
27840      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27841      */
27842     /**
27843      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27844      * the dropdown list (defaults to undefined, with no header element)
27845      */
27846
27847      /**
27848      * @cfg {String/Roo.Template} tpl The template to use to render the output
27849      */
27850      
27851     // private
27852     defaultAutoCreate : {tag: "select"  },
27853     /**
27854      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27855      */
27856     listWidth: undefined,
27857     /**
27858      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27859      * mode = 'remote' or 'text' if mode = 'local')
27860      */
27861     displayField: undefined,
27862     /**
27863      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27864      * mode = 'remote' or 'value' if mode = 'local'). 
27865      * Note: use of a valueField requires the user make a selection
27866      * in order for a value to be mapped.
27867      */
27868     valueField: undefined,
27869     
27870     
27871     /**
27872      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27873      * field's data value (defaults to the underlying DOM element's name)
27874      */
27875     hiddenName: undefined,
27876     /**
27877      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27878      */
27879     listClass: '',
27880     /**
27881      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27882      */
27883     selectedClass: 'x-combo-selected',
27884     /**
27885      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27886      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27887      * which displays a downward arrow icon).
27888      */
27889     triggerClass : 'x-form-arrow-trigger',
27890     /**
27891      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27892      */
27893     shadow:'sides',
27894     /**
27895      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27896      * anchor positions (defaults to 'tl-bl')
27897      */
27898     listAlign: 'tl-bl?',
27899     /**
27900      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27901      */
27902     maxHeight: 300,
27903     /**
27904      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27905      * query specified by the allQuery config option (defaults to 'query')
27906      */
27907     triggerAction: 'query',
27908     /**
27909      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27910      * (defaults to 4, does not apply if editable = false)
27911      */
27912     minChars : 4,
27913     /**
27914      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27915      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27916      */
27917     typeAhead: false,
27918     /**
27919      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27920      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27921      */
27922     queryDelay: 500,
27923     /**
27924      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27925      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
27926      */
27927     pageSize: 0,
27928     /**
27929      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
27930      * when editable = true (defaults to false)
27931      */
27932     selectOnFocus:false,
27933     /**
27934      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27935      */
27936     queryParam: 'query',
27937     /**
27938      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
27939      * when mode = 'remote' (defaults to 'Loading...')
27940      */
27941     loadingText: 'Loading...',
27942     /**
27943      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27944      */
27945     resizable: false,
27946     /**
27947      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27948      */
27949     handleHeight : 8,
27950     /**
27951      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27952      * traditional select (defaults to true)
27953      */
27954     editable: true,
27955     /**
27956      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27957      */
27958     allQuery: '',
27959     /**
27960      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27961      */
27962     mode: 'remote',
27963     /**
27964      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27965      * listWidth has a higher value)
27966      */
27967     minListWidth : 70,
27968     /**
27969      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
27970      * allow the user to set arbitrary text into the field (defaults to false)
27971      */
27972     forceSelection:false,
27973     /**
27974      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
27975      * if typeAhead = true (defaults to 250)
27976      */
27977     typeAheadDelay : 250,
27978     /**
27979      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
27980      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
27981      */
27982     valueNotFoundText : undefined,
27983     
27984     /**
27985      * @cfg {String} defaultValue The value displayed after loading the store.
27986      */
27987     defaultValue: '',
27988     
27989     /**
27990      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
27991      */
27992     blockFocus : false,
27993     
27994     /**
27995      * @cfg {Boolean} disableClear Disable showing of clear button.
27996      */
27997     disableClear : false,
27998     /**
27999      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
28000      */
28001     alwaysQuery : false,
28002     
28003     //private
28004     addicon : false,
28005     editicon: false,
28006     
28007     // element that contains real text value.. (when hidden is used..)
28008      
28009     // private
28010     onRender : function(ct, position){
28011         Roo.form.Field.prototype.onRender.call(this, ct, position);
28012         
28013         if(this.store){
28014             this.store.on('beforeload', this.onBeforeLoad, this);
28015             this.store.on('load', this.onLoad, this);
28016             this.store.on('loadexception', this.onLoadException, this);
28017             this.store.load({});
28018         }
28019         
28020         
28021         
28022     },
28023
28024     // private
28025     initEvents : function(){
28026         //Roo.form.ComboBox.superclass.initEvents.call(this);
28027  
28028     },
28029
28030     onDestroy : function(){
28031        
28032         if(this.store){
28033             this.store.un('beforeload', this.onBeforeLoad, this);
28034             this.store.un('load', this.onLoad, this);
28035             this.store.un('loadexception', this.onLoadException, this);
28036         }
28037         //Roo.form.ComboBox.superclass.onDestroy.call(this);
28038     },
28039
28040     // private
28041     fireKey : function(e){
28042         if(e.isNavKeyPress() && !this.list.isVisible()){
28043             this.fireEvent("specialkey", this, e);
28044         }
28045     },
28046
28047     // private
28048     onResize: function(w, h){
28049         
28050         return; 
28051     
28052         
28053     },
28054
28055     /**
28056      * Allow or prevent the user from directly editing the field text.  If false is passed,
28057      * the user will only be able to select from the items defined in the dropdown list.  This method
28058      * is the runtime equivalent of setting the 'editable' config option at config time.
28059      * @param {Boolean} value True to allow the user to directly edit the field text
28060      */
28061     setEditable : function(value){
28062          
28063     },
28064
28065     // private
28066     onBeforeLoad : function(){
28067         
28068         Roo.log("Select before load");
28069         return;
28070     
28071         this.innerList.update(this.loadingText ?
28072                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28073         //this.restrictHeight();
28074         this.selectedIndex = -1;
28075     },
28076
28077     // private
28078     onLoad : function(){
28079
28080     
28081         var dom = this.el.dom;
28082         dom.innerHTML = '';
28083          var od = dom.ownerDocument;
28084          
28085         if (this.emptyText) {
28086             var op = od.createElement('option');
28087             op.setAttribute('value', '');
28088             op.innerHTML = String.format('{0}', this.emptyText);
28089             dom.appendChild(op);
28090         }
28091         if(this.store.getCount() > 0){
28092            
28093             var vf = this.valueField;
28094             var df = this.displayField;
28095             this.store.data.each(function(r) {
28096                 // which colmsn to use... testing - cdoe / title..
28097                 var op = od.createElement('option');
28098                 op.setAttribute('value', r.data[vf]);
28099                 op.innerHTML = String.format('{0}', r.data[df]);
28100                 dom.appendChild(op);
28101             });
28102             if (typeof(this.defaultValue != 'undefined')) {
28103                 this.setValue(this.defaultValue);
28104             }
28105             
28106              
28107         }else{
28108             //this.onEmptyResults();
28109         }
28110         //this.el.focus();
28111     },
28112     // private
28113     onLoadException : function()
28114     {
28115         dom.innerHTML = '';
28116             
28117         Roo.log("Select on load exception");
28118         return;
28119     
28120         this.collapse();
28121         Roo.log(this.store.reader.jsonData);
28122         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28123             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28124         }
28125         
28126         
28127     },
28128     // private
28129     onTypeAhead : function(){
28130          
28131     },
28132
28133     // private
28134     onSelect : function(record, index){
28135         Roo.log('on select?');
28136         return;
28137         if(this.fireEvent('beforeselect', this, record, index) !== false){
28138             this.setFromData(index > -1 ? record.data : false);
28139             this.collapse();
28140             this.fireEvent('select', this, record, index);
28141         }
28142     },
28143
28144     /**
28145      * Returns the currently selected field value or empty string if no value is set.
28146      * @return {String} value The selected value
28147      */
28148     getValue : function(){
28149         var dom = this.el.dom;
28150         this.value = dom.options[dom.selectedIndex].value;
28151         return this.value;
28152         
28153     },
28154
28155     /**
28156      * Clears any text/value currently set in the field
28157      */
28158     clearValue : function(){
28159         this.value = '';
28160         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28161         
28162     },
28163
28164     /**
28165      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28166      * will be displayed in the field.  If the value does not match the data value of an existing item,
28167      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28168      * Otherwise the field will be blank (although the value will still be set).
28169      * @param {String} value The value to match
28170      */
28171     setValue : function(v){
28172         var d = this.el.dom;
28173         for (var i =0; i < d.options.length;i++) {
28174             if (v == d.options[i].value) {
28175                 d.selectedIndex = i;
28176                 this.value = v;
28177                 return;
28178             }
28179         }
28180         this.clearValue();
28181     },
28182     /**
28183      * @property {Object} the last set data for the element
28184      */
28185     
28186     lastData : false,
28187     /**
28188      * Sets the value of the field based on a object which is related to the record format for the store.
28189      * @param {Object} value the value to set as. or false on reset?
28190      */
28191     setFromData : function(o){
28192         Roo.log('setfrom data?');
28193          
28194         
28195         
28196     },
28197     // private
28198     reset : function(){
28199         this.clearValue();
28200     },
28201     // private
28202     findRecord : function(prop, value){
28203         
28204         return false;
28205     
28206         var record;
28207         if(this.store.getCount() > 0){
28208             this.store.each(function(r){
28209                 if(r.data[prop] == value){
28210                     record = r;
28211                     return false;
28212                 }
28213                 return true;
28214             });
28215         }
28216         return record;
28217     },
28218     
28219     getName: function()
28220     {
28221         // returns hidden if it's set..
28222         if (!this.rendered) {return ''};
28223         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28224         
28225     },
28226      
28227
28228     
28229
28230     // private
28231     onEmptyResults : function(){
28232         Roo.log('empty results');
28233         //this.collapse();
28234     },
28235
28236     /**
28237      * Returns true if the dropdown list is expanded, else false.
28238      */
28239     isExpanded : function(){
28240         return false;
28241     },
28242
28243     /**
28244      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28245      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28246      * @param {String} value The data value of the item to select
28247      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28248      * selected item if it is not currently in view (defaults to true)
28249      * @return {Boolean} True if the value matched an item in the list, else false
28250      */
28251     selectByValue : function(v, scrollIntoView){
28252         Roo.log('select By Value');
28253         return false;
28254     
28255         if(v !== undefined && v !== null){
28256             var r = this.findRecord(this.valueField || this.displayField, v);
28257             if(r){
28258                 this.select(this.store.indexOf(r), scrollIntoView);
28259                 return true;
28260             }
28261         }
28262         return false;
28263     },
28264
28265     /**
28266      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28267      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28268      * @param {Number} index The zero-based index of the list item to select
28269      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28270      * selected item if it is not currently in view (defaults to true)
28271      */
28272     select : function(index, scrollIntoView){
28273         Roo.log('select ');
28274         return  ;
28275         
28276         this.selectedIndex = index;
28277         this.view.select(index);
28278         if(scrollIntoView !== false){
28279             var el = this.view.getNode(index);
28280             if(el){
28281                 this.innerList.scrollChildIntoView(el, false);
28282             }
28283         }
28284     },
28285
28286       
28287
28288     // private
28289     validateBlur : function(){
28290         
28291         return;
28292         
28293     },
28294
28295     // private
28296     initQuery : function(){
28297         this.doQuery(this.getRawValue());
28298     },
28299
28300     // private
28301     doForce : function(){
28302         if(this.el.dom.value.length > 0){
28303             this.el.dom.value =
28304                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28305              
28306         }
28307     },
28308
28309     /**
28310      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28311      * query allowing the query action to be canceled if needed.
28312      * @param {String} query The SQL query to execute
28313      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28314      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28315      * saved in the current store (defaults to false)
28316      */
28317     doQuery : function(q, forceAll){
28318         
28319         Roo.log('doQuery?');
28320         if(q === undefined || q === null){
28321             q = '';
28322         }
28323         var qe = {
28324             query: q,
28325             forceAll: forceAll,
28326             combo: this,
28327             cancel:false
28328         };
28329         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28330             return false;
28331         }
28332         q = qe.query;
28333         forceAll = qe.forceAll;
28334         if(forceAll === true || (q.length >= this.minChars)){
28335             if(this.lastQuery != q || this.alwaysQuery){
28336                 this.lastQuery = q;
28337                 if(this.mode == 'local'){
28338                     this.selectedIndex = -1;
28339                     if(forceAll){
28340                         this.store.clearFilter();
28341                     }else{
28342                         this.store.filter(this.displayField, q);
28343                     }
28344                     this.onLoad();
28345                 }else{
28346                     this.store.baseParams[this.queryParam] = q;
28347                     this.store.load({
28348                         params: this.getParams(q)
28349                     });
28350                     this.expand();
28351                 }
28352             }else{
28353                 this.selectedIndex = -1;
28354                 this.onLoad();   
28355             }
28356         }
28357     },
28358
28359     // private
28360     getParams : function(q){
28361         var p = {};
28362         //p[this.queryParam] = q;
28363         if(this.pageSize){
28364             p.start = 0;
28365             p.limit = this.pageSize;
28366         }
28367         return p;
28368     },
28369
28370     /**
28371      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28372      */
28373     collapse : function(){
28374         
28375     },
28376
28377     // private
28378     collapseIf : function(e){
28379         
28380     },
28381
28382     /**
28383      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28384      */
28385     expand : function(){
28386         
28387     } ,
28388
28389     // private
28390      
28391
28392     /** 
28393     * @cfg {Boolean} grow 
28394     * @hide 
28395     */
28396     /** 
28397     * @cfg {Number} growMin 
28398     * @hide 
28399     */
28400     /** 
28401     * @cfg {Number} growMax 
28402     * @hide 
28403     */
28404     /**
28405      * @hide
28406      * @method autoSize
28407      */
28408     
28409     setWidth : function()
28410     {
28411         
28412     },
28413     getResizeEl : function(){
28414         return this.el;
28415     }
28416 });//<script type="text/javasscript">
28417  
28418
28419 /**
28420  * @class Roo.DDView
28421  * A DnD enabled version of Roo.View.
28422  * @param {Element/String} container The Element in which to create the View.
28423  * @param {String} tpl The template string used to create the markup for each element of the View
28424  * @param {Object} config The configuration properties. These include all the config options of
28425  * {@link Roo.View} plus some specific to this class.<br>
28426  * <p>
28427  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28428  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28429  * <p>
28430  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28431 .x-view-drag-insert-above {
28432         border-top:1px dotted #3366cc;
28433 }
28434 .x-view-drag-insert-below {
28435         border-bottom:1px dotted #3366cc;
28436 }
28437 </code></pre>
28438  * 
28439  */
28440  
28441 Roo.DDView = function(container, tpl, config) {
28442     Roo.DDView.superclass.constructor.apply(this, arguments);
28443     this.getEl().setStyle("outline", "0px none");
28444     this.getEl().unselectable();
28445     if (this.dragGroup) {
28446                 this.setDraggable(this.dragGroup.split(","));
28447     }
28448     if (this.dropGroup) {
28449                 this.setDroppable(this.dropGroup.split(","));
28450     }
28451     if (this.deletable) {
28452         this.setDeletable();
28453     }
28454     this.isDirtyFlag = false;
28455         this.addEvents({
28456                 "drop" : true
28457         });
28458 };
28459
28460 Roo.extend(Roo.DDView, Roo.View, {
28461 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28462 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28463 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28464 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28465
28466         isFormField: true,
28467
28468         reset: Roo.emptyFn,
28469         
28470         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28471
28472         validate: function() {
28473                 return true;
28474         },
28475         
28476         destroy: function() {
28477                 this.purgeListeners();
28478                 this.getEl.removeAllListeners();
28479                 this.getEl().remove();
28480                 if (this.dragZone) {
28481                         if (this.dragZone.destroy) {
28482                                 this.dragZone.destroy();
28483                         }
28484                 }
28485                 if (this.dropZone) {
28486                         if (this.dropZone.destroy) {
28487                                 this.dropZone.destroy();
28488                         }
28489                 }
28490         },
28491
28492 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28493         getName: function() {
28494                 return this.name;
28495         },
28496
28497 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28498         setValue: function(v) {
28499                 if (!this.store) {
28500                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28501                 }
28502                 var data = {};
28503                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28504                 this.store.proxy = new Roo.data.MemoryProxy(data);
28505                 this.store.load();
28506         },
28507
28508 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28509         getValue: function() {
28510                 var result = '(';
28511                 this.store.each(function(rec) {
28512                         result += rec.id + ',';
28513                 });
28514                 return result.substr(0, result.length - 1) + ')';
28515         },
28516         
28517         getIds: function() {
28518                 var i = 0, result = new Array(this.store.getCount());
28519                 this.store.each(function(rec) {
28520                         result[i++] = rec.id;
28521                 });
28522                 return result;
28523         },
28524         
28525         isDirty: function() {
28526                 return this.isDirtyFlag;
28527         },
28528
28529 /**
28530  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28531  *      whole Element becomes the target, and this causes the drop gesture to append.
28532  */
28533     getTargetFromEvent : function(e) {
28534                 var target = e.getTarget();
28535                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28536                 target = target.parentNode;
28537                 }
28538                 if (!target) {
28539                         target = this.el.dom.lastChild || this.el.dom;
28540                 }
28541                 return target;
28542     },
28543
28544 /**
28545  *      Create the drag data which consists of an object which has the property "ddel" as
28546  *      the drag proxy element. 
28547  */
28548     getDragData : function(e) {
28549         var target = this.findItemFromChild(e.getTarget());
28550                 if(target) {
28551                         this.handleSelection(e);
28552                         var selNodes = this.getSelectedNodes();
28553             var dragData = {
28554                 source: this,
28555                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28556                 nodes: selNodes,
28557                 records: []
28558                         };
28559                         var selectedIndices = this.getSelectedIndexes();
28560                         for (var i = 0; i < selectedIndices.length; i++) {
28561                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28562                         }
28563                         if (selNodes.length == 1) {
28564                                 dragData.ddel = target.cloneNode(true); // the div element
28565                         } else {
28566                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28567                                 div.className = 'multi-proxy';
28568                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28569                                         div.appendChild(selNodes[i].cloneNode(true));
28570                                 }
28571                                 dragData.ddel = div;
28572                         }
28573             //console.log(dragData)
28574             //console.log(dragData.ddel.innerHTML)
28575                         return dragData;
28576                 }
28577         //console.log('nodragData')
28578                 return false;
28579     },
28580     
28581 /**     Specify to which ddGroup items in this DDView may be dragged. */
28582     setDraggable: function(ddGroup) {
28583         if (ddGroup instanceof Array) {
28584                 Roo.each(ddGroup, this.setDraggable, this);
28585                 return;
28586         }
28587         if (this.dragZone) {
28588                 this.dragZone.addToGroup(ddGroup);
28589         } else {
28590                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28591                                 containerScroll: true,
28592                                 ddGroup: ddGroup 
28593
28594                         });
28595 //                      Draggability implies selection. DragZone's mousedown selects the element.
28596                         if (!this.multiSelect) { this.singleSelect = true; }
28597
28598 //                      Wire the DragZone's handlers up to methods in *this*
28599                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28600                 }
28601     },
28602
28603 /**     Specify from which ddGroup this DDView accepts drops. */
28604     setDroppable: function(ddGroup) {
28605         if (ddGroup instanceof Array) {
28606                 Roo.each(ddGroup, this.setDroppable, this);
28607                 return;
28608         }
28609         if (this.dropZone) {
28610                 this.dropZone.addToGroup(ddGroup);
28611         } else {
28612                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28613                                 containerScroll: true,
28614                                 ddGroup: ddGroup
28615                         });
28616
28617 //                      Wire the DropZone's handlers up to methods in *this*
28618                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28619                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28620                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28621                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28622                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28623                 }
28624     },
28625
28626 /**     Decide whether to drop above or below a View node. */
28627     getDropPoint : function(e, n, dd){
28628         if (n == this.el.dom) { return "above"; }
28629                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28630                 var c = t + (b - t) / 2;
28631                 var y = Roo.lib.Event.getPageY(e);
28632                 if(y <= c) {
28633                         return "above";
28634                 }else{
28635                         return "below";
28636                 }
28637     },
28638
28639     onNodeEnter : function(n, dd, e, data){
28640                 return false;
28641     },
28642     
28643     onNodeOver : function(n, dd, e, data){
28644                 var pt = this.getDropPoint(e, n, dd);
28645                 // set the insert point style on the target node
28646                 var dragElClass = this.dropNotAllowed;
28647                 if (pt) {
28648                         var targetElClass;
28649                         if (pt == "above"){
28650                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28651                                 targetElClass = "x-view-drag-insert-above";
28652                         } else {
28653                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28654                                 targetElClass = "x-view-drag-insert-below";
28655                         }
28656                         if (this.lastInsertClass != targetElClass){
28657                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28658                                 this.lastInsertClass = targetElClass;
28659                         }
28660                 }
28661                 return dragElClass;
28662         },
28663
28664     onNodeOut : function(n, dd, e, data){
28665                 this.removeDropIndicators(n);
28666     },
28667
28668     onNodeDrop : function(n, dd, e, data){
28669         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28670                 return false;
28671         }
28672         var pt = this.getDropPoint(e, n, dd);
28673                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28674                 if (pt == "below") { insertAt++; }
28675                 for (var i = 0; i < data.records.length; i++) {
28676                         var r = data.records[i];
28677                         var dup = this.store.getById(r.id);
28678                         if (dup && (dd != this.dragZone)) {
28679                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28680                         } else {
28681                                 if (data.copy) {
28682                                         this.store.insert(insertAt++, r.copy());
28683                                 } else {
28684                                         data.source.isDirtyFlag = true;
28685                                         r.store.remove(r);
28686                                         this.store.insert(insertAt++, r);
28687                                 }
28688                                 this.isDirtyFlag = true;
28689                         }
28690                 }
28691                 this.dragZone.cachedTarget = null;
28692                 return true;
28693     },
28694
28695     removeDropIndicators : function(n){
28696                 if(n){
28697                         Roo.fly(n).removeClass([
28698                                 "x-view-drag-insert-above",
28699                                 "x-view-drag-insert-below"]);
28700                         this.lastInsertClass = "_noclass";
28701                 }
28702     },
28703
28704 /**
28705  *      Utility method. Add a delete option to the DDView's context menu.
28706  *      @param {String} imageUrl The URL of the "delete" icon image.
28707  */
28708         setDeletable: function(imageUrl) {
28709                 if (!this.singleSelect && !this.multiSelect) {
28710                         this.singleSelect = true;
28711                 }
28712                 var c = this.getContextMenu();
28713                 this.contextMenu.on("itemclick", function(item) {
28714                         switch (item.id) {
28715                                 case "delete":
28716                                         this.remove(this.getSelectedIndexes());
28717                                         break;
28718                         }
28719                 }, this);
28720                 this.contextMenu.add({
28721                         icon: imageUrl,
28722                         id: "delete",
28723                         text: 'Delete'
28724                 });
28725         },
28726         
28727 /**     Return the context menu for this DDView. */
28728         getContextMenu: function() {
28729                 if (!this.contextMenu) {
28730 //                      Create the View's context menu
28731                         this.contextMenu = new Roo.menu.Menu({
28732                                 id: this.id + "-contextmenu"
28733                         });
28734                         this.el.on("contextmenu", this.showContextMenu, this);
28735                 }
28736                 return this.contextMenu;
28737         },
28738         
28739         disableContextMenu: function() {
28740                 if (this.contextMenu) {
28741                         this.el.un("contextmenu", this.showContextMenu, this);
28742                 }
28743         },
28744
28745         showContextMenu: function(e, item) {
28746         item = this.findItemFromChild(e.getTarget());
28747                 if (item) {
28748                         e.stopEvent();
28749                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28750                         this.contextMenu.showAt(e.getXY());
28751             }
28752     },
28753
28754 /**
28755  *      Remove {@link Roo.data.Record}s at the specified indices.
28756  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28757  */
28758     remove: function(selectedIndices) {
28759                 selectedIndices = [].concat(selectedIndices);
28760                 for (var i = 0; i < selectedIndices.length; i++) {
28761                         var rec = this.store.getAt(selectedIndices[i]);
28762                         this.store.remove(rec);
28763                 }
28764     },
28765
28766 /**
28767  *      Double click fires the event, but also, if this is draggable, and there is only one other
28768  *      related DropZone, it transfers the selected node.
28769  */
28770     onDblClick : function(e){
28771         var item = this.findItemFromChild(e.getTarget());
28772         if(item){
28773             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28774                 return false;
28775             }
28776             if (this.dragGroup) {
28777                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28778                     while (targets.indexOf(this.dropZone) > -1) {
28779                             targets.remove(this.dropZone);
28780                                 }
28781                     if (targets.length == 1) {
28782                                         this.dragZone.cachedTarget = null;
28783                         var el = Roo.get(targets[0].getEl());
28784                         var box = el.getBox(true);
28785                         targets[0].onNodeDrop(el.dom, {
28786                                 target: el.dom,
28787                                 xy: [box.x, box.y + box.height - 1]
28788                         }, null, this.getDragData(e));
28789                     }
28790                 }
28791         }
28792     },
28793     
28794     handleSelection: function(e) {
28795                 this.dragZone.cachedTarget = null;
28796         var item = this.findItemFromChild(e.getTarget());
28797         if (!item) {
28798                 this.clearSelections(true);
28799                 return;
28800         }
28801                 if (item && (this.multiSelect || this.singleSelect)){
28802                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28803                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28804                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28805                                 this.unselect(item);
28806                         } else {
28807                                 this.select(item, this.multiSelect && e.ctrlKey);
28808                                 this.lastSelection = item;
28809                         }
28810                 }
28811     },
28812
28813     onItemClick : function(item, index, e){
28814                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28815                         return false;
28816                 }
28817                 return true;
28818     },
28819
28820     unselect : function(nodeInfo, suppressEvent){
28821                 var node = this.getNode(nodeInfo);
28822                 if(node && this.isSelected(node)){
28823                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28824                                 Roo.fly(node).removeClass(this.selectedClass);
28825                                 this.selections.remove(node);
28826                                 if(!suppressEvent){
28827                                         this.fireEvent("selectionchange", this, this.selections);
28828                                 }
28829                         }
28830                 }
28831     }
28832 });
28833 /*
28834  * Based on:
28835  * Ext JS Library 1.1.1
28836  * Copyright(c) 2006-2007, Ext JS, LLC.
28837  *
28838  * Originally Released Under LGPL - original licence link has changed is not relivant.
28839  *
28840  * Fork - LGPL
28841  * <script type="text/javascript">
28842  */
28843  
28844 /**
28845  * @class Roo.LayoutManager
28846  * @extends Roo.util.Observable
28847  * Base class for layout managers.
28848  */
28849 Roo.LayoutManager = function(container, config){
28850     Roo.LayoutManager.superclass.constructor.call(this);
28851     this.el = Roo.get(container);
28852     // ie scrollbar fix
28853     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28854         document.body.scroll = "no";
28855     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28856         this.el.position('relative');
28857     }
28858     this.id = this.el.id;
28859     this.el.addClass("x-layout-container");
28860     /** false to disable window resize monitoring @type Boolean */
28861     this.monitorWindowResize = true;
28862     this.regions = {};
28863     this.addEvents({
28864         /**
28865          * @event layout
28866          * Fires when a layout is performed. 
28867          * @param {Roo.LayoutManager} this
28868          */
28869         "layout" : true,
28870         /**
28871          * @event regionresized
28872          * Fires when the user resizes a region. 
28873          * @param {Roo.LayoutRegion} region The resized region
28874          * @param {Number} newSize The new size (width for east/west, height for north/south)
28875          */
28876         "regionresized" : true,
28877         /**
28878          * @event regioncollapsed
28879          * Fires when a region is collapsed. 
28880          * @param {Roo.LayoutRegion} region The collapsed region
28881          */
28882         "regioncollapsed" : true,
28883         /**
28884          * @event regionexpanded
28885          * Fires when a region is expanded.  
28886          * @param {Roo.LayoutRegion} region The expanded region
28887          */
28888         "regionexpanded" : true
28889     });
28890     this.updating = false;
28891     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28892 };
28893
28894 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28895     /**
28896      * Returns true if this layout is currently being updated
28897      * @return {Boolean}
28898      */
28899     isUpdating : function(){
28900         return this.updating; 
28901     },
28902     
28903     /**
28904      * Suspend the LayoutManager from doing auto-layouts while
28905      * making multiple add or remove calls
28906      */
28907     beginUpdate : function(){
28908         this.updating = true;    
28909     },
28910     
28911     /**
28912      * Restore auto-layouts and optionally disable the manager from performing a layout
28913      * @param {Boolean} noLayout true to disable a layout update 
28914      */
28915     endUpdate : function(noLayout){
28916         this.updating = false;
28917         if(!noLayout){
28918             this.layout();
28919         }    
28920     },
28921     
28922     layout: function(){
28923         
28924     },
28925     
28926     onRegionResized : function(region, newSize){
28927         this.fireEvent("regionresized", region, newSize);
28928         this.layout();
28929     },
28930     
28931     onRegionCollapsed : function(region){
28932         this.fireEvent("regioncollapsed", region);
28933     },
28934     
28935     onRegionExpanded : function(region){
28936         this.fireEvent("regionexpanded", region);
28937     },
28938         
28939     /**
28940      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28941      * performs box-model adjustments.
28942      * @return {Object} The size as an object {width: (the width), height: (the height)}
28943      */
28944     getViewSize : function(){
28945         var size;
28946         if(this.el.dom != document.body){
28947             size = this.el.getSize();
28948         }else{
28949             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28950         }
28951         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28952         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28953         return size;
28954     },
28955     
28956     /**
28957      * Returns the Element this layout is bound to.
28958      * @return {Roo.Element}
28959      */
28960     getEl : function(){
28961         return this.el;
28962     },
28963     
28964     /**
28965      * Returns the specified region.
28966      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28967      * @return {Roo.LayoutRegion}
28968      */
28969     getRegion : function(target){
28970         return this.regions[target.toLowerCase()];
28971     },
28972     
28973     onWindowResize : function(){
28974         if(this.monitorWindowResize){
28975             this.layout();
28976         }
28977     }
28978 });/*
28979  * Based on:
28980  * Ext JS Library 1.1.1
28981  * Copyright(c) 2006-2007, Ext JS, LLC.
28982  *
28983  * Originally Released Under LGPL - original licence link has changed is not relivant.
28984  *
28985  * Fork - LGPL
28986  * <script type="text/javascript">
28987  */
28988 /**
28989  * @class Roo.BorderLayout
28990  * @extends Roo.LayoutManager
28991  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28992  * please see: <br><br>
28993  * <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>
28994  * <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>
28995  * Example:
28996  <pre><code>
28997  var layout = new Roo.BorderLayout(document.body, {
28998     north: {
28999         initialSize: 25,
29000         titlebar: false
29001     },
29002     west: {
29003         split:true,
29004         initialSize: 200,
29005         minSize: 175,
29006         maxSize: 400,
29007         titlebar: true,
29008         collapsible: true
29009     },
29010     east: {
29011         split:true,
29012         initialSize: 202,
29013         minSize: 175,
29014         maxSize: 400,
29015         titlebar: true,
29016         collapsible: true
29017     },
29018     south: {
29019         split:true,
29020         initialSize: 100,
29021         minSize: 100,
29022         maxSize: 200,
29023         titlebar: true,
29024         collapsible: true
29025     },
29026     center: {
29027         titlebar: true,
29028         autoScroll:true,
29029         resizeTabs: true,
29030         minTabWidth: 50,
29031         preferredTabWidth: 150
29032     }
29033 });
29034
29035 // shorthand
29036 var CP = Roo.ContentPanel;
29037
29038 layout.beginUpdate();
29039 layout.add("north", new CP("north", "North"));
29040 layout.add("south", new CP("south", {title: "South", closable: true}));
29041 layout.add("west", new CP("west", {title: "West"}));
29042 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29043 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29044 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29045 layout.getRegion("center").showPanel("center1");
29046 layout.endUpdate();
29047 </code></pre>
29048
29049 <b>The container the layout is rendered into can be either the body element or any other element.
29050 If it is not the body element, the container needs to either be an absolute positioned element,
29051 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29052 the container size if it is not the body element.</b>
29053
29054 * @constructor
29055 * Create a new BorderLayout
29056 * @param {String/HTMLElement/Element} container The container this layout is bound to
29057 * @param {Object} config Configuration options
29058  */
29059 Roo.BorderLayout = function(container, config){
29060     config = config || {};
29061     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29062     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29063     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29064         var target = this.factory.validRegions[i];
29065         if(config[target]){
29066             this.addRegion(target, config[target]);
29067         }
29068     }
29069 };
29070
29071 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29072     /**
29073      * Creates and adds a new region if it doesn't already exist.
29074      * @param {String} target The target region key (north, south, east, west or center).
29075      * @param {Object} config The regions config object
29076      * @return {BorderLayoutRegion} The new region
29077      */
29078     addRegion : function(target, config){
29079         if(!this.regions[target]){
29080             var r = this.factory.create(target, this, config);
29081             this.bindRegion(target, r);
29082         }
29083         return this.regions[target];
29084     },
29085
29086     // private (kinda)
29087     bindRegion : function(name, r){
29088         this.regions[name] = r;
29089         r.on("visibilitychange", this.layout, this);
29090         r.on("paneladded", this.layout, this);
29091         r.on("panelremoved", this.layout, this);
29092         r.on("invalidated", this.layout, this);
29093         r.on("resized", this.onRegionResized, this);
29094         r.on("collapsed", this.onRegionCollapsed, this);
29095         r.on("expanded", this.onRegionExpanded, this);
29096     },
29097
29098     /**
29099      * Performs a layout update.
29100      */
29101     layout : function(){
29102         if(this.updating) {
29103             return;
29104         }
29105         var size = this.getViewSize();
29106         var w = size.width;
29107         var h = size.height;
29108         var centerW = w;
29109         var centerH = h;
29110         var centerY = 0;
29111         var centerX = 0;
29112         //var x = 0, y = 0;
29113
29114         var rs = this.regions;
29115         var north = rs["north"];
29116         var south = rs["south"]; 
29117         var west = rs["west"];
29118         var east = rs["east"];
29119         var center = rs["center"];
29120         //if(this.hideOnLayout){ // not supported anymore
29121             //c.el.setStyle("display", "none");
29122         //}
29123         if(north && north.isVisible()){
29124             var b = north.getBox();
29125             var m = north.getMargins();
29126             b.width = w - (m.left+m.right);
29127             b.x = m.left;
29128             b.y = m.top;
29129             centerY = b.height + b.y + m.bottom;
29130             centerH -= centerY;
29131             north.updateBox(this.safeBox(b));
29132         }
29133         if(south && south.isVisible()){
29134             var b = south.getBox();
29135             var m = south.getMargins();
29136             b.width = w - (m.left+m.right);
29137             b.x = m.left;
29138             var totalHeight = (b.height + m.top + m.bottom);
29139             b.y = h - totalHeight + m.top;
29140             centerH -= totalHeight;
29141             south.updateBox(this.safeBox(b));
29142         }
29143         if(west && west.isVisible()){
29144             var b = west.getBox();
29145             var m = west.getMargins();
29146             b.height = centerH - (m.top+m.bottom);
29147             b.x = m.left;
29148             b.y = centerY + m.top;
29149             var totalWidth = (b.width + m.left + m.right);
29150             centerX += totalWidth;
29151             centerW -= totalWidth;
29152             west.updateBox(this.safeBox(b));
29153         }
29154         if(east && east.isVisible()){
29155             var b = east.getBox();
29156             var m = east.getMargins();
29157             b.height = centerH - (m.top+m.bottom);
29158             var totalWidth = (b.width + m.left + m.right);
29159             b.x = w - totalWidth + m.left;
29160             b.y = centerY + m.top;
29161             centerW -= totalWidth;
29162             east.updateBox(this.safeBox(b));
29163         }
29164         if(center){
29165             var m = center.getMargins();
29166             var centerBox = {
29167                 x: centerX + m.left,
29168                 y: centerY + m.top,
29169                 width: centerW - (m.left+m.right),
29170                 height: centerH - (m.top+m.bottom)
29171             };
29172             //if(this.hideOnLayout){
29173                 //center.el.setStyle("display", "block");
29174             //}
29175             center.updateBox(this.safeBox(centerBox));
29176         }
29177         this.el.repaint();
29178         this.fireEvent("layout", this);
29179     },
29180
29181     // private
29182     safeBox : function(box){
29183         box.width = Math.max(0, box.width);
29184         box.height = Math.max(0, box.height);
29185         return box;
29186     },
29187
29188     /**
29189      * Adds a ContentPanel (or subclass) to this layout.
29190      * @param {String} target The target region key (north, south, east, west or center).
29191      * @param {Roo.ContentPanel} panel The panel to add
29192      * @return {Roo.ContentPanel} The added panel
29193      */
29194     add : function(target, panel){
29195          
29196         target = target.toLowerCase();
29197         return this.regions[target].add(panel);
29198     },
29199
29200     /**
29201      * Remove a ContentPanel (or subclass) to this layout.
29202      * @param {String} target The target region key (north, south, east, west or center).
29203      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29204      * @return {Roo.ContentPanel} The removed panel
29205      */
29206     remove : function(target, panel){
29207         target = target.toLowerCase();
29208         return this.regions[target].remove(panel);
29209     },
29210
29211     /**
29212      * Searches all regions for a panel with the specified id
29213      * @param {String} panelId
29214      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29215      */
29216     findPanel : function(panelId){
29217         var rs = this.regions;
29218         for(var target in rs){
29219             if(typeof rs[target] != "function"){
29220                 var p = rs[target].getPanel(panelId);
29221                 if(p){
29222                     return p;
29223                 }
29224             }
29225         }
29226         return null;
29227     },
29228
29229     /**
29230      * Searches all regions for a panel with the specified id and activates (shows) it.
29231      * @param {String/ContentPanel} panelId The panels id or the panel itself
29232      * @return {Roo.ContentPanel} The shown panel or null
29233      */
29234     showPanel : function(panelId) {
29235       var rs = this.regions;
29236       for(var target in rs){
29237          var r = rs[target];
29238          if(typeof r != "function"){
29239             if(r.hasPanel(panelId)){
29240                return r.showPanel(panelId);
29241             }
29242          }
29243       }
29244       return null;
29245    },
29246
29247    /**
29248      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29249      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29250      */
29251     restoreState : function(provider){
29252         if(!provider){
29253             provider = Roo.state.Manager;
29254         }
29255         var sm = new Roo.LayoutStateManager();
29256         sm.init(this, provider);
29257     },
29258
29259     /**
29260      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29261      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29262      * a valid ContentPanel config object.  Example:
29263      * <pre><code>
29264 // Create the main layout
29265 var layout = new Roo.BorderLayout('main-ct', {
29266     west: {
29267         split:true,
29268         minSize: 175,
29269         titlebar: true
29270     },
29271     center: {
29272         title:'Components'
29273     }
29274 }, 'main-ct');
29275
29276 // Create and add multiple ContentPanels at once via configs
29277 layout.batchAdd({
29278    west: {
29279        id: 'source-files',
29280        autoCreate:true,
29281        title:'Ext Source Files',
29282        autoScroll:true,
29283        fitToFrame:true
29284    },
29285    center : {
29286        el: cview,
29287        autoScroll:true,
29288        fitToFrame:true,
29289        toolbar: tb,
29290        resizeEl:'cbody'
29291    }
29292 });
29293 </code></pre>
29294      * @param {Object} regions An object containing ContentPanel configs by region name
29295      */
29296     batchAdd : function(regions){
29297         this.beginUpdate();
29298         for(var rname in regions){
29299             var lr = this.regions[rname];
29300             if(lr){
29301                 this.addTypedPanels(lr, regions[rname]);
29302             }
29303         }
29304         this.endUpdate();
29305     },
29306
29307     // private
29308     addTypedPanels : function(lr, ps){
29309         if(typeof ps == 'string'){
29310             lr.add(new Roo.ContentPanel(ps));
29311         }
29312         else if(ps instanceof Array){
29313             for(var i =0, len = ps.length; i < len; i++){
29314                 this.addTypedPanels(lr, ps[i]);
29315             }
29316         }
29317         else if(!ps.events){ // raw config?
29318             var el = ps.el;
29319             delete ps.el; // prevent conflict
29320             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29321         }
29322         else {  // panel object assumed!
29323             lr.add(ps);
29324         }
29325     },
29326     /**
29327      * Adds a xtype elements to the layout.
29328      * <pre><code>
29329
29330 layout.addxtype({
29331        xtype : 'ContentPanel',
29332        region: 'west',
29333        items: [ .... ]
29334    }
29335 );
29336
29337 layout.addxtype({
29338         xtype : 'NestedLayoutPanel',
29339         region: 'west',
29340         layout: {
29341            center: { },
29342            west: { }   
29343         },
29344         items : [ ... list of content panels or nested layout panels.. ]
29345    }
29346 );
29347 </code></pre>
29348      * @param {Object} cfg Xtype definition of item to add.
29349      */
29350     addxtype : function(cfg)
29351     {
29352         // basically accepts a pannel...
29353         // can accept a layout region..!?!?
29354         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29355         
29356         if (!cfg.xtype.match(/Panel$/)) {
29357             return false;
29358         }
29359         var ret = false;
29360         
29361         if (typeof(cfg.region) == 'undefined') {
29362             Roo.log("Failed to add Panel, region was not set");
29363             Roo.log(cfg);
29364             return false;
29365         }
29366         var region = cfg.region;
29367         delete cfg.region;
29368         
29369           
29370         var xitems = [];
29371         if (cfg.items) {
29372             xitems = cfg.items;
29373             delete cfg.items;
29374         }
29375         var nb = false;
29376         
29377         switch(cfg.xtype) 
29378         {
29379             case 'ContentPanel':  // ContentPanel (el, cfg)
29380             case 'ScrollPanel':  // ContentPanel (el, cfg)
29381             case 'ViewPanel': 
29382                 if(cfg.autoCreate) {
29383                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29384                 } else {
29385                     var el = this.el.createChild();
29386                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29387                 }
29388                 
29389                 this.add(region, ret);
29390                 break;
29391             
29392             
29393             case 'TreePanel': // our new panel!
29394                 cfg.el = this.el.createChild();
29395                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29396                 this.add(region, ret);
29397                 break;
29398             
29399             case 'NestedLayoutPanel': 
29400                 // create a new Layout (which is  a Border Layout...
29401                 var el = this.el.createChild();
29402                 var clayout = cfg.layout;
29403                 delete cfg.layout;
29404                 clayout.items   = clayout.items  || [];
29405                 // replace this exitems with the clayout ones..
29406                 xitems = clayout.items;
29407                  
29408                 
29409                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29410                     cfg.background = false;
29411                 }
29412                 var layout = new Roo.BorderLayout(el, clayout);
29413                 
29414                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29415                 //console.log('adding nested layout panel '  + cfg.toSource());
29416                 this.add(region, ret);
29417                 nb = {}; /// find first...
29418                 break;
29419                 
29420             case 'GridPanel': 
29421             
29422                 // needs grid and region
29423                 
29424                 //var el = this.getRegion(region).el.createChild();
29425                 var el = this.el.createChild();
29426                 // create the grid first...
29427                 
29428                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29429                 delete cfg.grid;
29430                 if (region == 'center' && this.active ) {
29431                     cfg.background = false;
29432                 }
29433                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29434                 
29435                 this.add(region, ret);
29436                 if (cfg.background) {
29437                     ret.on('activate', function(gp) {
29438                         if (!gp.grid.rendered) {
29439                             gp.grid.render();
29440                         }
29441                     });
29442                 } else {
29443                     grid.render();
29444                 }
29445                 break;
29446            
29447            
29448            
29449                 
29450                 
29451                 
29452             default:
29453                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29454                     
29455                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29456                     this.add(region, ret);
29457                 } else {
29458                 
29459                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29460                     return null;
29461                 }
29462                 
29463              // GridPanel (grid, cfg)
29464             
29465         }
29466         this.beginUpdate();
29467         // add children..
29468         var region = '';
29469         var abn = {};
29470         Roo.each(xitems, function(i)  {
29471             region = nb && i.region ? i.region : false;
29472             
29473             var add = ret.addxtype(i);
29474            
29475             if (region) {
29476                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29477                 if (!i.background) {
29478                     abn[region] = nb[region] ;
29479                 }
29480             }
29481             
29482         });
29483         this.endUpdate();
29484
29485         // make the last non-background panel active..
29486         //if (nb) { Roo.log(abn); }
29487         if (nb) {
29488             
29489             for(var r in abn) {
29490                 region = this.getRegion(r);
29491                 if (region) {
29492                     // tried using nb[r], but it does not work..
29493                      
29494                     region.showPanel(abn[r]);
29495                    
29496                 }
29497             }
29498         }
29499         return ret;
29500         
29501     }
29502 });
29503
29504 /**
29505  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29506  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29507  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29508  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29509  * <pre><code>
29510 // shorthand
29511 var CP = Roo.ContentPanel;
29512
29513 var layout = Roo.BorderLayout.create({
29514     north: {
29515         initialSize: 25,
29516         titlebar: false,
29517         panels: [new CP("north", "North")]
29518     },
29519     west: {
29520         split:true,
29521         initialSize: 200,
29522         minSize: 175,
29523         maxSize: 400,
29524         titlebar: true,
29525         collapsible: true,
29526         panels: [new CP("west", {title: "West"})]
29527     },
29528     east: {
29529         split:true,
29530         initialSize: 202,
29531         minSize: 175,
29532         maxSize: 400,
29533         titlebar: true,
29534         collapsible: true,
29535         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29536     },
29537     south: {
29538         split:true,
29539         initialSize: 100,
29540         minSize: 100,
29541         maxSize: 200,
29542         titlebar: true,
29543         collapsible: true,
29544         panels: [new CP("south", {title: "South", closable: true})]
29545     },
29546     center: {
29547         titlebar: true,
29548         autoScroll:true,
29549         resizeTabs: true,
29550         minTabWidth: 50,
29551         preferredTabWidth: 150,
29552         panels: [
29553             new CP("center1", {title: "Close Me", closable: true}),
29554             new CP("center2", {title: "Center Panel", closable: false})
29555         ]
29556     }
29557 }, document.body);
29558
29559 layout.getRegion("center").showPanel("center1");
29560 </code></pre>
29561  * @param config
29562  * @param targetEl
29563  */
29564 Roo.BorderLayout.create = function(config, targetEl){
29565     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29566     layout.beginUpdate();
29567     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29568     for(var j = 0, jlen = regions.length; j < jlen; j++){
29569         var lr = regions[j];
29570         if(layout.regions[lr] && config[lr].panels){
29571             var r = layout.regions[lr];
29572             var ps = config[lr].panels;
29573             layout.addTypedPanels(r, ps);
29574         }
29575     }
29576     layout.endUpdate();
29577     return layout;
29578 };
29579
29580 // private
29581 Roo.BorderLayout.RegionFactory = {
29582     // private
29583     validRegions : ["north","south","east","west","center"],
29584
29585     // private
29586     create : function(target, mgr, config){
29587         target = target.toLowerCase();
29588         if(config.lightweight || config.basic){
29589             return new Roo.BasicLayoutRegion(mgr, config, target);
29590         }
29591         switch(target){
29592             case "north":
29593                 return new Roo.NorthLayoutRegion(mgr, config);
29594             case "south":
29595                 return new Roo.SouthLayoutRegion(mgr, config);
29596             case "east":
29597                 return new Roo.EastLayoutRegion(mgr, config);
29598             case "west":
29599                 return new Roo.WestLayoutRegion(mgr, config);
29600             case "center":
29601                 return new Roo.CenterLayoutRegion(mgr, config);
29602         }
29603         throw 'Layout region "'+target+'" not supported.';
29604     }
29605 };/*
29606  * Based on:
29607  * Ext JS Library 1.1.1
29608  * Copyright(c) 2006-2007, Ext JS, LLC.
29609  *
29610  * Originally Released Under LGPL - original licence link has changed is not relivant.
29611  *
29612  * Fork - LGPL
29613  * <script type="text/javascript">
29614  */
29615  
29616 /**
29617  * @class Roo.BasicLayoutRegion
29618  * @extends Roo.util.Observable
29619  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29620  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29621  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29622  */
29623 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29624     this.mgr = mgr;
29625     this.position  = pos;
29626     this.events = {
29627         /**
29628          * @scope Roo.BasicLayoutRegion
29629          */
29630         
29631         /**
29632          * @event beforeremove
29633          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29634          * @param {Roo.LayoutRegion} this
29635          * @param {Roo.ContentPanel} panel The panel
29636          * @param {Object} e The cancel event object
29637          */
29638         "beforeremove" : true,
29639         /**
29640          * @event invalidated
29641          * Fires when the layout for this region is changed.
29642          * @param {Roo.LayoutRegion} this
29643          */
29644         "invalidated" : true,
29645         /**
29646          * @event visibilitychange
29647          * Fires when this region is shown or hidden 
29648          * @param {Roo.LayoutRegion} this
29649          * @param {Boolean} visibility true or false
29650          */
29651         "visibilitychange" : true,
29652         /**
29653          * @event paneladded
29654          * Fires when a panel is added. 
29655          * @param {Roo.LayoutRegion} this
29656          * @param {Roo.ContentPanel} panel The panel
29657          */
29658         "paneladded" : true,
29659         /**
29660          * @event panelremoved
29661          * Fires when a panel is removed. 
29662          * @param {Roo.LayoutRegion} this
29663          * @param {Roo.ContentPanel} panel The panel
29664          */
29665         "panelremoved" : true,
29666         /**
29667          * @event beforecollapse
29668          * Fires when this region before collapse.
29669          * @param {Roo.LayoutRegion} this
29670          */
29671         "beforecollapse" : true,
29672         /**
29673          * @event collapsed
29674          * Fires when this region is collapsed.
29675          * @param {Roo.LayoutRegion} this
29676          */
29677         "collapsed" : true,
29678         /**
29679          * @event expanded
29680          * Fires when this region is expanded.
29681          * @param {Roo.LayoutRegion} this
29682          */
29683         "expanded" : true,
29684         /**
29685          * @event slideshow
29686          * Fires when this region is slid into view.
29687          * @param {Roo.LayoutRegion} this
29688          */
29689         "slideshow" : true,
29690         /**
29691          * @event slidehide
29692          * Fires when this region slides out of view. 
29693          * @param {Roo.LayoutRegion} this
29694          */
29695         "slidehide" : true,
29696         /**
29697          * @event panelactivated
29698          * Fires when a panel is activated. 
29699          * @param {Roo.LayoutRegion} this
29700          * @param {Roo.ContentPanel} panel The activated panel
29701          */
29702         "panelactivated" : true,
29703         /**
29704          * @event resized
29705          * Fires when the user resizes this region. 
29706          * @param {Roo.LayoutRegion} this
29707          * @param {Number} newSize The new size (width for east/west, height for north/south)
29708          */
29709         "resized" : true
29710     };
29711     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29712     this.panels = new Roo.util.MixedCollection();
29713     this.panels.getKey = this.getPanelId.createDelegate(this);
29714     this.box = null;
29715     this.activePanel = null;
29716     // ensure listeners are added...
29717     
29718     if (config.listeners || config.events) {
29719         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29720             listeners : config.listeners || {},
29721             events : config.events || {}
29722         });
29723     }
29724     
29725     if(skipConfig !== true){
29726         this.applyConfig(config);
29727     }
29728 };
29729
29730 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29731     getPanelId : function(p){
29732         return p.getId();
29733     },
29734     
29735     applyConfig : function(config){
29736         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29737         this.config = config;
29738         
29739     },
29740     
29741     /**
29742      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29743      * the width, for horizontal (north, south) the height.
29744      * @param {Number} newSize The new width or height
29745      */
29746     resizeTo : function(newSize){
29747         var el = this.el ? this.el :
29748                  (this.activePanel ? this.activePanel.getEl() : null);
29749         if(el){
29750             switch(this.position){
29751                 case "east":
29752                 case "west":
29753                     el.setWidth(newSize);
29754                     this.fireEvent("resized", this, newSize);
29755                 break;
29756                 case "north":
29757                 case "south":
29758                     el.setHeight(newSize);
29759                     this.fireEvent("resized", this, newSize);
29760                 break;                
29761             }
29762         }
29763     },
29764     
29765     getBox : function(){
29766         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29767     },
29768     
29769     getMargins : function(){
29770         return this.margins;
29771     },
29772     
29773     updateBox : function(box){
29774         this.box = box;
29775         var el = this.activePanel.getEl();
29776         el.dom.style.left = box.x + "px";
29777         el.dom.style.top = box.y + "px";
29778         this.activePanel.setSize(box.width, box.height);
29779     },
29780     
29781     /**
29782      * Returns the container element for this region.
29783      * @return {Roo.Element}
29784      */
29785     getEl : function(){
29786         return this.activePanel;
29787     },
29788     
29789     /**
29790      * Returns true if this region is currently visible.
29791      * @return {Boolean}
29792      */
29793     isVisible : function(){
29794         return this.activePanel ? true : false;
29795     },
29796     
29797     setActivePanel : function(panel){
29798         panel = this.getPanel(panel);
29799         if(this.activePanel && this.activePanel != panel){
29800             this.activePanel.setActiveState(false);
29801             this.activePanel.getEl().setLeftTop(-10000,-10000);
29802         }
29803         this.activePanel = panel;
29804         panel.setActiveState(true);
29805         if(this.box){
29806             panel.setSize(this.box.width, this.box.height);
29807         }
29808         this.fireEvent("panelactivated", this, panel);
29809         this.fireEvent("invalidated");
29810     },
29811     
29812     /**
29813      * Show the specified panel.
29814      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29815      * @return {Roo.ContentPanel} The shown panel or null
29816      */
29817     showPanel : function(panel){
29818         if(panel = this.getPanel(panel)){
29819             this.setActivePanel(panel);
29820         }
29821         return panel;
29822     },
29823     
29824     /**
29825      * Get the active panel for this region.
29826      * @return {Roo.ContentPanel} The active panel or null
29827      */
29828     getActivePanel : function(){
29829         return this.activePanel;
29830     },
29831     
29832     /**
29833      * Add the passed ContentPanel(s)
29834      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29835      * @return {Roo.ContentPanel} The panel added (if only one was added)
29836      */
29837     add : function(panel){
29838         if(arguments.length > 1){
29839             for(var i = 0, len = arguments.length; i < len; i++) {
29840                 this.add(arguments[i]);
29841             }
29842             return null;
29843         }
29844         if(this.hasPanel(panel)){
29845             this.showPanel(panel);
29846             return panel;
29847         }
29848         var el = panel.getEl();
29849         if(el.dom.parentNode != this.mgr.el.dom){
29850             this.mgr.el.dom.appendChild(el.dom);
29851         }
29852         if(panel.setRegion){
29853             panel.setRegion(this);
29854         }
29855         this.panels.add(panel);
29856         el.setStyle("position", "absolute");
29857         if(!panel.background){
29858             this.setActivePanel(panel);
29859             if(this.config.initialSize && this.panels.getCount()==1){
29860                 this.resizeTo(this.config.initialSize);
29861             }
29862         }
29863         this.fireEvent("paneladded", this, panel);
29864         return panel;
29865     },
29866     
29867     /**
29868      * Returns true if the panel is in this region.
29869      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29870      * @return {Boolean}
29871      */
29872     hasPanel : function(panel){
29873         if(typeof panel == "object"){ // must be panel obj
29874             panel = panel.getId();
29875         }
29876         return this.getPanel(panel) ? true : false;
29877     },
29878     
29879     /**
29880      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29881      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29882      * @param {Boolean} preservePanel Overrides the config preservePanel option
29883      * @return {Roo.ContentPanel} The panel that was removed
29884      */
29885     remove : function(panel, preservePanel){
29886         panel = this.getPanel(panel);
29887         if(!panel){
29888             return null;
29889         }
29890         var e = {};
29891         this.fireEvent("beforeremove", this, panel, e);
29892         if(e.cancel === true){
29893             return null;
29894         }
29895         var panelId = panel.getId();
29896         this.panels.removeKey(panelId);
29897         return panel;
29898     },
29899     
29900     /**
29901      * Returns the panel specified or null if it's not in this region.
29902      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29903      * @return {Roo.ContentPanel}
29904      */
29905     getPanel : function(id){
29906         if(typeof id == "object"){ // must be panel obj
29907             return id;
29908         }
29909         return this.panels.get(id);
29910     },
29911     
29912     /**
29913      * Returns this regions position (north/south/east/west/center).
29914      * @return {String} 
29915      */
29916     getPosition: function(){
29917         return this.position;    
29918     }
29919 });/*
29920  * Based on:
29921  * Ext JS Library 1.1.1
29922  * Copyright(c) 2006-2007, Ext JS, LLC.
29923  *
29924  * Originally Released Under LGPL - original licence link has changed is not relivant.
29925  *
29926  * Fork - LGPL
29927  * <script type="text/javascript">
29928  */
29929  
29930 /**
29931  * @class Roo.LayoutRegion
29932  * @extends Roo.BasicLayoutRegion
29933  * This class represents a region in a layout manager.
29934  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29935  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29936  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29937  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29938  * @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})
29939  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
29940  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29941  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29942  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29943  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29944  * @cfg {String}    title           The title for the region (overrides panel titles)
29945  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29946  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29947  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29948  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29949  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29950  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29951  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29952  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29953  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29954  * @cfg {Boolean}   showPin         True to show a pin button
29955  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29956  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29957  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29958  * @cfg {Number}    width           For East/West panels
29959  * @cfg {Number}    height          For North/South panels
29960  * @cfg {Boolean}   split           To show the splitter
29961  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29962  */
29963 Roo.LayoutRegion = function(mgr, config, pos){
29964     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29965     var dh = Roo.DomHelper;
29966     /** This region's container element 
29967     * @type Roo.Element */
29968     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29969     /** This region's title element 
29970     * @type Roo.Element */
29971
29972     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29973         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29974         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29975     ]}, true);
29976     this.titleEl.enableDisplayMode();
29977     /** This region's title text element 
29978     * @type HTMLElement */
29979     this.titleTextEl = this.titleEl.dom.firstChild;
29980     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29981     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29982     this.closeBtn.enableDisplayMode();
29983     this.closeBtn.on("click", this.closeClicked, this);
29984     this.closeBtn.hide();
29985
29986     this.createBody(config);
29987     this.visible = true;
29988     this.collapsed = false;
29989
29990     if(config.hideWhenEmpty){
29991         this.hide();
29992         this.on("paneladded", this.validateVisibility, this);
29993         this.on("panelremoved", this.validateVisibility, this);
29994     }
29995     this.applyConfig(config);
29996 };
29997
29998 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29999
30000     createBody : function(){
30001         /** This region's body element 
30002         * @type Roo.Element */
30003         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30004     },
30005
30006     applyConfig : function(c){
30007         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30008             var dh = Roo.DomHelper;
30009             if(c.titlebar !== false){
30010                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30011                 this.collapseBtn.on("click", this.collapse, this);
30012                 this.collapseBtn.enableDisplayMode();
30013
30014                 if(c.showPin === true || this.showPin){
30015                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30016                     this.stickBtn.enableDisplayMode();
30017                     this.stickBtn.on("click", this.expand, this);
30018                     this.stickBtn.hide();
30019                 }
30020             }
30021             /** This region's collapsed element
30022             * @type Roo.Element */
30023             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30024                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30025             ]}, true);
30026             if(c.floatable !== false){
30027                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30028                this.collapsedEl.on("click", this.collapseClick, this);
30029             }
30030
30031             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30032                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30033                    id: "message", unselectable: "on", style:{"float":"left"}});
30034                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30035              }
30036             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30037             this.expandBtn.on("click", this.expand, this);
30038         }
30039         if(this.collapseBtn){
30040             this.collapseBtn.setVisible(c.collapsible == true);
30041         }
30042         this.cmargins = c.cmargins || this.cmargins ||
30043                          (this.position == "west" || this.position == "east" ?
30044                              {top: 0, left: 2, right:2, bottom: 0} :
30045                              {top: 2, left: 0, right:0, bottom: 2});
30046         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30047         this.bottomTabs = c.tabPosition != "top";
30048         this.autoScroll = c.autoScroll || false;
30049         if(this.autoScroll){
30050             this.bodyEl.setStyle("overflow", "auto");
30051         }else{
30052             this.bodyEl.setStyle("overflow", "hidden");
30053         }
30054         //if(c.titlebar !== false){
30055             if((!c.titlebar && !c.title) || c.titlebar === false){
30056                 this.titleEl.hide();
30057             }else{
30058                 this.titleEl.show();
30059                 if(c.title){
30060                     this.titleTextEl.innerHTML = c.title;
30061                 }
30062             }
30063         //}
30064         this.duration = c.duration || .30;
30065         this.slideDuration = c.slideDuration || .45;
30066         this.config = c;
30067         if(c.collapsed){
30068             this.collapse(true);
30069         }
30070         if(c.hidden){
30071             this.hide();
30072         }
30073     },
30074     /**
30075      * Returns true if this region is currently visible.
30076      * @return {Boolean}
30077      */
30078     isVisible : function(){
30079         return this.visible;
30080     },
30081
30082     /**
30083      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30084      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30085      */
30086     setCollapsedTitle : function(title){
30087         title = title || "&#160;";
30088         if(this.collapsedTitleTextEl){
30089             this.collapsedTitleTextEl.innerHTML = title;
30090         }
30091     },
30092
30093     getBox : function(){
30094         var b;
30095         if(!this.collapsed){
30096             b = this.el.getBox(false, true);
30097         }else{
30098             b = this.collapsedEl.getBox(false, true);
30099         }
30100         return b;
30101     },
30102
30103     getMargins : function(){
30104         return this.collapsed ? this.cmargins : this.margins;
30105     },
30106
30107     highlight : function(){
30108         this.el.addClass("x-layout-panel-dragover");
30109     },
30110
30111     unhighlight : function(){
30112         this.el.removeClass("x-layout-panel-dragover");
30113     },
30114
30115     updateBox : function(box){
30116         this.box = box;
30117         if(!this.collapsed){
30118             this.el.dom.style.left = box.x + "px";
30119             this.el.dom.style.top = box.y + "px";
30120             this.updateBody(box.width, box.height);
30121         }else{
30122             this.collapsedEl.dom.style.left = box.x + "px";
30123             this.collapsedEl.dom.style.top = box.y + "px";
30124             this.collapsedEl.setSize(box.width, box.height);
30125         }
30126         if(this.tabs){
30127             this.tabs.autoSizeTabs();
30128         }
30129     },
30130
30131     updateBody : function(w, h){
30132         if(w !== null){
30133             this.el.setWidth(w);
30134             w -= this.el.getBorderWidth("rl");
30135             if(this.config.adjustments){
30136                 w += this.config.adjustments[0];
30137             }
30138         }
30139         if(h !== null){
30140             this.el.setHeight(h);
30141             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30142             h -= this.el.getBorderWidth("tb");
30143             if(this.config.adjustments){
30144                 h += this.config.adjustments[1];
30145             }
30146             this.bodyEl.setHeight(h);
30147             if(this.tabs){
30148                 h = this.tabs.syncHeight(h);
30149             }
30150         }
30151         if(this.panelSize){
30152             w = w !== null ? w : this.panelSize.width;
30153             h = h !== null ? h : this.panelSize.height;
30154         }
30155         if(this.activePanel){
30156             var el = this.activePanel.getEl();
30157             w = w !== null ? w : el.getWidth();
30158             h = h !== null ? h : el.getHeight();
30159             this.panelSize = {width: w, height: h};
30160             this.activePanel.setSize(w, h);
30161         }
30162         if(Roo.isIE && this.tabs){
30163             this.tabs.el.repaint();
30164         }
30165     },
30166
30167     /**
30168      * Returns the container element for this region.
30169      * @return {Roo.Element}
30170      */
30171     getEl : function(){
30172         return this.el;
30173     },
30174
30175     /**
30176      * Hides this region.
30177      */
30178     hide : function(){
30179         if(!this.collapsed){
30180             this.el.dom.style.left = "-2000px";
30181             this.el.hide();
30182         }else{
30183             this.collapsedEl.dom.style.left = "-2000px";
30184             this.collapsedEl.hide();
30185         }
30186         this.visible = false;
30187         this.fireEvent("visibilitychange", this, false);
30188     },
30189
30190     /**
30191      * Shows this region if it was previously hidden.
30192      */
30193     show : function(){
30194         if(!this.collapsed){
30195             this.el.show();
30196         }else{
30197             this.collapsedEl.show();
30198         }
30199         this.visible = true;
30200         this.fireEvent("visibilitychange", this, true);
30201     },
30202
30203     closeClicked : function(){
30204         if(this.activePanel){
30205             this.remove(this.activePanel);
30206         }
30207     },
30208
30209     collapseClick : function(e){
30210         if(this.isSlid){
30211            e.stopPropagation();
30212            this.slideIn();
30213         }else{
30214            e.stopPropagation();
30215            this.slideOut();
30216         }
30217     },
30218
30219     /**
30220      * Collapses this region.
30221      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30222      */
30223     collapse : function(skipAnim, skipCheck){
30224         if(this.collapsed) {
30225             return;
30226         }
30227         
30228         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30229             
30230             this.collapsed = true;
30231             if(this.split){
30232                 this.split.el.hide();
30233             }
30234             if(this.config.animate && skipAnim !== true){
30235                 this.fireEvent("invalidated", this);
30236                 this.animateCollapse();
30237             }else{
30238                 this.el.setLocation(-20000,-20000);
30239                 this.el.hide();
30240                 this.collapsedEl.show();
30241                 this.fireEvent("collapsed", this);
30242                 this.fireEvent("invalidated", this);
30243             }
30244         }
30245         
30246     },
30247
30248     animateCollapse : function(){
30249         // overridden
30250     },
30251
30252     /**
30253      * Expands this region if it was previously collapsed.
30254      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30255      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30256      */
30257     expand : function(e, skipAnim){
30258         if(e) {
30259             e.stopPropagation();
30260         }
30261         if(!this.collapsed || this.el.hasActiveFx()) {
30262             return;
30263         }
30264         if(this.isSlid){
30265             this.afterSlideIn();
30266             skipAnim = true;
30267         }
30268         this.collapsed = false;
30269         if(this.config.animate && skipAnim !== true){
30270             this.animateExpand();
30271         }else{
30272             this.el.show();
30273             if(this.split){
30274                 this.split.el.show();
30275             }
30276             this.collapsedEl.setLocation(-2000,-2000);
30277             this.collapsedEl.hide();
30278             this.fireEvent("invalidated", this);
30279             this.fireEvent("expanded", this);
30280         }
30281     },
30282
30283     animateExpand : function(){
30284         // overridden
30285     },
30286
30287     initTabs : function()
30288     {
30289         this.bodyEl.setStyle("overflow", "hidden");
30290         var ts = new Roo.TabPanel(
30291                 this.bodyEl.dom,
30292                 {
30293                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30294                     disableTooltips: this.config.disableTabTips,
30295                     toolbar : this.config.toolbar
30296                 }
30297         );
30298         if(this.config.hideTabs){
30299             ts.stripWrap.setDisplayed(false);
30300         }
30301         this.tabs = ts;
30302         ts.resizeTabs = this.config.resizeTabs === true;
30303         ts.minTabWidth = this.config.minTabWidth || 40;
30304         ts.maxTabWidth = this.config.maxTabWidth || 250;
30305         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30306         ts.monitorResize = false;
30307         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30308         ts.bodyEl.addClass('x-layout-tabs-body');
30309         this.panels.each(this.initPanelAsTab, this);
30310     },
30311
30312     initPanelAsTab : function(panel){
30313         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30314                     this.config.closeOnTab && panel.isClosable());
30315         if(panel.tabTip !== undefined){
30316             ti.setTooltip(panel.tabTip);
30317         }
30318         ti.on("activate", function(){
30319               this.setActivePanel(panel);
30320         }, this);
30321         if(this.config.closeOnTab){
30322             ti.on("beforeclose", function(t, e){
30323                 e.cancel = true;
30324                 this.remove(panel);
30325             }, this);
30326         }
30327         return ti;
30328     },
30329
30330     updatePanelTitle : function(panel, title){
30331         if(this.activePanel == panel){
30332             this.updateTitle(title);
30333         }
30334         if(this.tabs){
30335             var ti = this.tabs.getTab(panel.getEl().id);
30336             ti.setText(title);
30337             if(panel.tabTip !== undefined){
30338                 ti.setTooltip(panel.tabTip);
30339             }
30340         }
30341     },
30342
30343     updateTitle : function(title){
30344         if(this.titleTextEl && !this.config.title){
30345             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30346         }
30347     },
30348
30349     setActivePanel : function(panel){
30350         panel = this.getPanel(panel);
30351         if(this.activePanel && this.activePanel != panel){
30352             this.activePanel.setActiveState(false);
30353         }
30354         this.activePanel = panel;
30355         panel.setActiveState(true);
30356         if(this.panelSize){
30357             panel.setSize(this.panelSize.width, this.panelSize.height);
30358         }
30359         if(this.closeBtn){
30360             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30361         }
30362         this.updateTitle(panel.getTitle());
30363         if(this.tabs){
30364             this.fireEvent("invalidated", this);
30365         }
30366         this.fireEvent("panelactivated", this, panel);
30367     },
30368
30369     /**
30370      * Shows the specified panel.
30371      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30372      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30373      */
30374     showPanel : function(panel)
30375     {
30376         panel = this.getPanel(panel);
30377         if(panel){
30378             if(this.tabs){
30379                 var tab = this.tabs.getTab(panel.getEl().id);
30380                 if(tab.isHidden()){
30381                     this.tabs.unhideTab(tab.id);
30382                 }
30383                 tab.activate();
30384             }else{
30385                 this.setActivePanel(panel);
30386             }
30387         }
30388         return panel;
30389     },
30390
30391     /**
30392      * Get the active panel for this region.
30393      * @return {Roo.ContentPanel} The active panel or null
30394      */
30395     getActivePanel : function(){
30396         return this.activePanel;
30397     },
30398
30399     validateVisibility : function(){
30400         if(this.panels.getCount() < 1){
30401             this.updateTitle("&#160;");
30402             this.closeBtn.hide();
30403             this.hide();
30404         }else{
30405             if(!this.isVisible()){
30406                 this.show();
30407             }
30408         }
30409     },
30410
30411     /**
30412      * Adds the passed ContentPanel(s) to this region.
30413      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30414      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30415      */
30416     add : function(panel){
30417         if(arguments.length > 1){
30418             for(var i = 0, len = arguments.length; i < len; i++) {
30419                 this.add(arguments[i]);
30420             }
30421             return null;
30422         }
30423         if(this.hasPanel(panel)){
30424             this.showPanel(panel);
30425             return panel;
30426         }
30427         panel.setRegion(this);
30428         this.panels.add(panel);
30429         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30430             this.bodyEl.dom.appendChild(panel.getEl().dom);
30431             if(panel.background !== true){
30432                 this.setActivePanel(panel);
30433             }
30434             this.fireEvent("paneladded", this, panel);
30435             return panel;
30436         }
30437         if(!this.tabs){
30438             this.initTabs();
30439         }else{
30440             this.initPanelAsTab(panel);
30441         }
30442         if(panel.background !== true){
30443             this.tabs.activate(panel.getEl().id);
30444         }
30445         this.fireEvent("paneladded", this, panel);
30446         return panel;
30447     },
30448
30449     /**
30450      * Hides the tab for the specified panel.
30451      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30452      */
30453     hidePanel : function(panel){
30454         if(this.tabs && (panel = this.getPanel(panel))){
30455             this.tabs.hideTab(panel.getEl().id);
30456         }
30457     },
30458
30459     /**
30460      * Unhides the tab for a previously hidden panel.
30461      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30462      */
30463     unhidePanel : function(panel){
30464         if(this.tabs && (panel = this.getPanel(panel))){
30465             this.tabs.unhideTab(panel.getEl().id);
30466         }
30467     },
30468
30469     clearPanels : function(){
30470         while(this.panels.getCount() > 0){
30471              this.remove(this.panels.first());
30472         }
30473     },
30474
30475     /**
30476      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30477      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30478      * @param {Boolean} preservePanel Overrides the config preservePanel option
30479      * @return {Roo.ContentPanel} The panel that was removed
30480      */
30481     remove : function(panel, preservePanel){
30482         panel = this.getPanel(panel);
30483         if(!panel){
30484             return null;
30485         }
30486         var e = {};
30487         this.fireEvent("beforeremove", this, panel, e);
30488         if(e.cancel === true){
30489             return null;
30490         }
30491         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30492         var panelId = panel.getId();
30493         this.panels.removeKey(panelId);
30494         if(preservePanel){
30495             document.body.appendChild(panel.getEl().dom);
30496         }
30497         if(this.tabs){
30498             this.tabs.removeTab(panel.getEl().id);
30499         }else if (!preservePanel){
30500             this.bodyEl.dom.removeChild(panel.getEl().dom);
30501         }
30502         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30503             var p = this.panels.first();
30504             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30505             tempEl.appendChild(p.getEl().dom);
30506             this.bodyEl.update("");
30507             this.bodyEl.dom.appendChild(p.getEl().dom);
30508             tempEl = null;
30509             this.updateTitle(p.getTitle());
30510             this.tabs = null;
30511             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30512             this.setActivePanel(p);
30513         }
30514         panel.setRegion(null);
30515         if(this.activePanel == panel){
30516             this.activePanel = null;
30517         }
30518         if(this.config.autoDestroy !== false && preservePanel !== true){
30519             try{panel.destroy();}catch(e){}
30520         }
30521         this.fireEvent("panelremoved", this, panel);
30522         return panel;
30523     },
30524
30525     /**
30526      * Returns the TabPanel component used by this region
30527      * @return {Roo.TabPanel}
30528      */
30529     getTabs : function(){
30530         return this.tabs;
30531     },
30532
30533     createTool : function(parentEl, className){
30534         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30535             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30536         btn.addClassOnOver("x-layout-tools-button-over");
30537         return btn;
30538     }
30539 });/*
30540  * Based on:
30541  * Ext JS Library 1.1.1
30542  * Copyright(c) 2006-2007, Ext JS, LLC.
30543  *
30544  * Originally Released Under LGPL - original licence link has changed is not relivant.
30545  *
30546  * Fork - LGPL
30547  * <script type="text/javascript">
30548  */
30549  
30550
30551
30552 /**
30553  * @class Roo.SplitLayoutRegion
30554  * @extends Roo.LayoutRegion
30555  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30556  */
30557 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30558     this.cursor = cursor;
30559     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30560 };
30561
30562 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30563     splitTip : "Drag to resize.",
30564     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30565     useSplitTips : false,
30566
30567     applyConfig : function(config){
30568         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30569         if(config.split){
30570             if(!this.split){
30571                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30572                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30573                 /** The SplitBar for this region 
30574                 * @type Roo.SplitBar */
30575                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30576                 this.split.on("moved", this.onSplitMove, this);
30577                 this.split.useShim = config.useShim === true;
30578                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30579                 if(this.useSplitTips){
30580                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30581                 }
30582                 if(config.collapsible){
30583                     this.split.el.on("dblclick", this.collapse,  this);
30584                 }
30585             }
30586             if(typeof config.minSize != "undefined"){
30587                 this.split.minSize = config.minSize;
30588             }
30589             if(typeof config.maxSize != "undefined"){
30590                 this.split.maxSize = config.maxSize;
30591             }
30592             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30593                 this.hideSplitter();
30594             }
30595         }
30596     },
30597
30598     getHMaxSize : function(){
30599          var cmax = this.config.maxSize || 10000;
30600          var center = this.mgr.getRegion("center");
30601          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30602     },
30603
30604     getVMaxSize : function(){
30605          var cmax = this.config.maxSize || 10000;
30606          var center = this.mgr.getRegion("center");
30607          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30608     },
30609
30610     onSplitMove : function(split, newSize){
30611         this.fireEvent("resized", this, newSize);
30612     },
30613     
30614     /** 
30615      * Returns the {@link Roo.SplitBar} for this region.
30616      * @return {Roo.SplitBar}
30617      */
30618     getSplitBar : function(){
30619         return this.split;
30620     },
30621     
30622     hide : function(){
30623         this.hideSplitter();
30624         Roo.SplitLayoutRegion.superclass.hide.call(this);
30625     },
30626
30627     hideSplitter : function(){
30628         if(this.split){
30629             this.split.el.setLocation(-2000,-2000);
30630             this.split.el.hide();
30631         }
30632     },
30633
30634     show : function(){
30635         if(this.split){
30636             this.split.el.show();
30637         }
30638         Roo.SplitLayoutRegion.superclass.show.call(this);
30639     },
30640     
30641     beforeSlide: function(){
30642         if(Roo.isGecko){// firefox overflow auto bug workaround
30643             this.bodyEl.clip();
30644             if(this.tabs) {
30645                 this.tabs.bodyEl.clip();
30646             }
30647             if(this.activePanel){
30648                 this.activePanel.getEl().clip();
30649                 
30650                 if(this.activePanel.beforeSlide){
30651                     this.activePanel.beforeSlide();
30652                 }
30653             }
30654         }
30655     },
30656     
30657     afterSlide : function(){
30658         if(Roo.isGecko){// firefox overflow auto bug workaround
30659             this.bodyEl.unclip();
30660             if(this.tabs) {
30661                 this.tabs.bodyEl.unclip();
30662             }
30663             if(this.activePanel){
30664                 this.activePanel.getEl().unclip();
30665                 if(this.activePanel.afterSlide){
30666                     this.activePanel.afterSlide();
30667                 }
30668             }
30669         }
30670     },
30671
30672     initAutoHide : function(){
30673         if(this.autoHide !== false){
30674             if(!this.autoHideHd){
30675                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30676                 this.autoHideHd = {
30677                     "mouseout": function(e){
30678                         if(!e.within(this.el, true)){
30679                             st.delay(500);
30680                         }
30681                     },
30682                     "mouseover" : function(e){
30683                         st.cancel();
30684                     },
30685                     scope : this
30686                 };
30687             }
30688             this.el.on(this.autoHideHd);
30689         }
30690     },
30691
30692     clearAutoHide : function(){
30693         if(this.autoHide !== false){
30694             this.el.un("mouseout", this.autoHideHd.mouseout);
30695             this.el.un("mouseover", this.autoHideHd.mouseover);
30696         }
30697     },
30698
30699     clearMonitor : function(){
30700         Roo.get(document).un("click", this.slideInIf, this);
30701     },
30702
30703     // these names are backwards but not changed for compat
30704     slideOut : function(){
30705         if(this.isSlid || this.el.hasActiveFx()){
30706             return;
30707         }
30708         this.isSlid = true;
30709         if(this.collapseBtn){
30710             this.collapseBtn.hide();
30711         }
30712         this.closeBtnState = this.closeBtn.getStyle('display');
30713         this.closeBtn.hide();
30714         if(this.stickBtn){
30715             this.stickBtn.show();
30716         }
30717         this.el.show();
30718         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30719         this.beforeSlide();
30720         this.el.setStyle("z-index", 10001);
30721         this.el.slideIn(this.getSlideAnchor(), {
30722             callback: function(){
30723                 this.afterSlide();
30724                 this.initAutoHide();
30725                 Roo.get(document).on("click", this.slideInIf, this);
30726                 this.fireEvent("slideshow", this);
30727             },
30728             scope: this,
30729             block: true
30730         });
30731     },
30732
30733     afterSlideIn : function(){
30734         this.clearAutoHide();
30735         this.isSlid = false;
30736         this.clearMonitor();
30737         this.el.setStyle("z-index", "");
30738         if(this.collapseBtn){
30739             this.collapseBtn.show();
30740         }
30741         this.closeBtn.setStyle('display', this.closeBtnState);
30742         if(this.stickBtn){
30743             this.stickBtn.hide();
30744         }
30745         this.fireEvent("slidehide", this);
30746     },
30747
30748     slideIn : function(cb){
30749         if(!this.isSlid || this.el.hasActiveFx()){
30750             Roo.callback(cb);
30751             return;
30752         }
30753         this.isSlid = false;
30754         this.beforeSlide();
30755         this.el.slideOut(this.getSlideAnchor(), {
30756             callback: function(){
30757                 this.el.setLeftTop(-10000, -10000);
30758                 this.afterSlide();
30759                 this.afterSlideIn();
30760                 Roo.callback(cb);
30761             },
30762             scope: this,
30763             block: true
30764         });
30765     },
30766     
30767     slideInIf : function(e){
30768         if(!e.within(this.el)){
30769             this.slideIn();
30770         }
30771     },
30772
30773     animateCollapse : function(){
30774         this.beforeSlide();
30775         this.el.setStyle("z-index", 20000);
30776         var anchor = this.getSlideAnchor();
30777         this.el.slideOut(anchor, {
30778             callback : function(){
30779                 this.el.setStyle("z-index", "");
30780                 this.collapsedEl.slideIn(anchor, {duration:.3});
30781                 this.afterSlide();
30782                 this.el.setLocation(-10000,-10000);
30783                 this.el.hide();
30784                 this.fireEvent("collapsed", this);
30785             },
30786             scope: this,
30787             block: true
30788         });
30789     },
30790
30791     animateExpand : function(){
30792         this.beforeSlide();
30793         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30794         this.el.setStyle("z-index", 20000);
30795         this.collapsedEl.hide({
30796             duration:.1
30797         });
30798         this.el.slideIn(this.getSlideAnchor(), {
30799             callback : function(){
30800                 this.el.setStyle("z-index", "");
30801                 this.afterSlide();
30802                 if(this.split){
30803                     this.split.el.show();
30804                 }
30805                 this.fireEvent("invalidated", this);
30806                 this.fireEvent("expanded", this);
30807             },
30808             scope: this,
30809             block: true
30810         });
30811     },
30812
30813     anchors : {
30814         "west" : "left",
30815         "east" : "right",
30816         "north" : "top",
30817         "south" : "bottom"
30818     },
30819
30820     sanchors : {
30821         "west" : "l",
30822         "east" : "r",
30823         "north" : "t",
30824         "south" : "b"
30825     },
30826
30827     canchors : {
30828         "west" : "tl-tr",
30829         "east" : "tr-tl",
30830         "north" : "tl-bl",
30831         "south" : "bl-tl"
30832     },
30833
30834     getAnchor : function(){
30835         return this.anchors[this.position];
30836     },
30837
30838     getCollapseAnchor : function(){
30839         return this.canchors[this.position];
30840     },
30841
30842     getSlideAnchor : function(){
30843         return this.sanchors[this.position];
30844     },
30845
30846     getAlignAdj : function(){
30847         var cm = this.cmargins;
30848         switch(this.position){
30849             case "west":
30850                 return [0, 0];
30851             break;
30852             case "east":
30853                 return [0, 0];
30854             break;
30855             case "north":
30856                 return [0, 0];
30857             break;
30858             case "south":
30859                 return [0, 0];
30860             break;
30861         }
30862     },
30863
30864     getExpandAdj : function(){
30865         var c = this.collapsedEl, cm = this.cmargins;
30866         switch(this.position){
30867             case "west":
30868                 return [-(cm.right+c.getWidth()+cm.left), 0];
30869             break;
30870             case "east":
30871                 return [cm.right+c.getWidth()+cm.left, 0];
30872             break;
30873             case "north":
30874                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30875             break;
30876             case "south":
30877                 return [0, cm.top+cm.bottom+c.getHeight()];
30878             break;
30879         }
30880     }
30881 });/*
30882  * Based on:
30883  * Ext JS Library 1.1.1
30884  * Copyright(c) 2006-2007, Ext JS, LLC.
30885  *
30886  * Originally Released Under LGPL - original licence link has changed is not relivant.
30887  *
30888  * Fork - LGPL
30889  * <script type="text/javascript">
30890  */
30891 /*
30892  * These classes are private internal classes
30893  */
30894 Roo.CenterLayoutRegion = function(mgr, config){
30895     Roo.LayoutRegion.call(this, mgr, config, "center");
30896     this.visible = true;
30897     this.minWidth = config.minWidth || 20;
30898     this.minHeight = config.minHeight || 20;
30899 };
30900
30901 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30902     hide : function(){
30903         // center panel can't be hidden
30904     },
30905     
30906     show : function(){
30907         // center panel can't be hidden
30908     },
30909     
30910     getMinWidth: function(){
30911         return this.minWidth;
30912     },
30913     
30914     getMinHeight: function(){
30915         return this.minHeight;
30916     }
30917 });
30918
30919
30920 Roo.NorthLayoutRegion = function(mgr, config){
30921     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30922     if(this.split){
30923         this.split.placement = Roo.SplitBar.TOP;
30924         this.split.orientation = Roo.SplitBar.VERTICAL;
30925         this.split.el.addClass("x-layout-split-v");
30926     }
30927     var size = config.initialSize || config.height;
30928     if(typeof size != "undefined"){
30929         this.el.setHeight(size);
30930     }
30931 };
30932 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30933     orientation: Roo.SplitBar.VERTICAL,
30934     getBox : function(){
30935         if(this.collapsed){
30936             return this.collapsedEl.getBox();
30937         }
30938         var box = this.el.getBox();
30939         if(this.split){
30940             box.height += this.split.el.getHeight();
30941         }
30942         return box;
30943     },
30944     
30945     updateBox : function(box){
30946         if(this.split && !this.collapsed){
30947             box.height -= this.split.el.getHeight();
30948             this.split.el.setLeft(box.x);
30949             this.split.el.setTop(box.y+box.height);
30950             this.split.el.setWidth(box.width);
30951         }
30952         if(this.collapsed){
30953             this.updateBody(box.width, null);
30954         }
30955         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30956     }
30957 });
30958
30959 Roo.SouthLayoutRegion = function(mgr, config){
30960     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30961     if(this.split){
30962         this.split.placement = Roo.SplitBar.BOTTOM;
30963         this.split.orientation = Roo.SplitBar.VERTICAL;
30964         this.split.el.addClass("x-layout-split-v");
30965     }
30966     var size = config.initialSize || config.height;
30967     if(typeof size != "undefined"){
30968         this.el.setHeight(size);
30969     }
30970 };
30971 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30972     orientation: Roo.SplitBar.VERTICAL,
30973     getBox : function(){
30974         if(this.collapsed){
30975             return this.collapsedEl.getBox();
30976         }
30977         var box = this.el.getBox();
30978         if(this.split){
30979             var sh = this.split.el.getHeight();
30980             box.height += sh;
30981             box.y -= sh;
30982         }
30983         return box;
30984     },
30985     
30986     updateBox : function(box){
30987         if(this.split && !this.collapsed){
30988             var sh = this.split.el.getHeight();
30989             box.height -= sh;
30990             box.y += sh;
30991             this.split.el.setLeft(box.x);
30992             this.split.el.setTop(box.y-sh);
30993             this.split.el.setWidth(box.width);
30994         }
30995         if(this.collapsed){
30996             this.updateBody(box.width, null);
30997         }
30998         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30999     }
31000 });
31001
31002 Roo.EastLayoutRegion = function(mgr, config){
31003     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31004     if(this.split){
31005         this.split.placement = Roo.SplitBar.RIGHT;
31006         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31007         this.split.el.addClass("x-layout-split-h");
31008     }
31009     var size = config.initialSize || config.width;
31010     if(typeof size != "undefined"){
31011         this.el.setWidth(size);
31012     }
31013 };
31014 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31015     orientation: Roo.SplitBar.HORIZONTAL,
31016     getBox : function(){
31017         if(this.collapsed){
31018             return this.collapsedEl.getBox();
31019         }
31020         var box = this.el.getBox();
31021         if(this.split){
31022             var sw = this.split.el.getWidth();
31023             box.width += sw;
31024             box.x -= sw;
31025         }
31026         return box;
31027     },
31028
31029     updateBox : function(box){
31030         if(this.split && !this.collapsed){
31031             var sw = this.split.el.getWidth();
31032             box.width -= sw;
31033             this.split.el.setLeft(box.x);
31034             this.split.el.setTop(box.y);
31035             this.split.el.setHeight(box.height);
31036             box.x += sw;
31037         }
31038         if(this.collapsed){
31039             this.updateBody(null, box.height);
31040         }
31041         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31042     }
31043 });
31044
31045 Roo.WestLayoutRegion = function(mgr, config){
31046     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31047     if(this.split){
31048         this.split.placement = Roo.SplitBar.LEFT;
31049         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31050         this.split.el.addClass("x-layout-split-h");
31051     }
31052     var size = config.initialSize || config.width;
31053     if(typeof size != "undefined"){
31054         this.el.setWidth(size);
31055     }
31056 };
31057 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31058     orientation: Roo.SplitBar.HORIZONTAL,
31059     getBox : function(){
31060         if(this.collapsed){
31061             return this.collapsedEl.getBox();
31062         }
31063         var box = this.el.getBox();
31064         if(this.split){
31065             box.width += this.split.el.getWidth();
31066         }
31067         return box;
31068     },
31069     
31070     updateBox : function(box){
31071         if(this.split && !this.collapsed){
31072             var sw = this.split.el.getWidth();
31073             box.width -= sw;
31074             this.split.el.setLeft(box.x+box.width);
31075             this.split.el.setTop(box.y);
31076             this.split.el.setHeight(box.height);
31077         }
31078         if(this.collapsed){
31079             this.updateBody(null, box.height);
31080         }
31081         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31082     }
31083 });
31084 /*
31085  * Based on:
31086  * Ext JS Library 1.1.1
31087  * Copyright(c) 2006-2007, Ext JS, LLC.
31088  *
31089  * Originally Released Under LGPL - original licence link has changed is not relivant.
31090  *
31091  * Fork - LGPL
31092  * <script type="text/javascript">
31093  */
31094  
31095  
31096 /*
31097  * Private internal class for reading and applying state
31098  */
31099 Roo.LayoutStateManager = function(layout){
31100      // default empty state
31101      this.state = {
31102         north: {},
31103         south: {},
31104         east: {},
31105         west: {}       
31106     };
31107 };
31108
31109 Roo.LayoutStateManager.prototype = {
31110     init : function(layout, provider){
31111         this.provider = provider;
31112         var state = provider.get(layout.id+"-layout-state");
31113         if(state){
31114             var wasUpdating = layout.isUpdating();
31115             if(!wasUpdating){
31116                 layout.beginUpdate();
31117             }
31118             for(var key in state){
31119                 if(typeof state[key] != "function"){
31120                     var rstate = state[key];
31121                     var r = layout.getRegion(key);
31122                     if(r && rstate){
31123                         if(rstate.size){
31124                             r.resizeTo(rstate.size);
31125                         }
31126                         if(rstate.collapsed == true){
31127                             r.collapse(true);
31128                         }else{
31129                             r.expand(null, true);
31130                         }
31131                     }
31132                 }
31133             }
31134             if(!wasUpdating){
31135                 layout.endUpdate();
31136             }
31137             this.state = state; 
31138         }
31139         this.layout = layout;
31140         layout.on("regionresized", this.onRegionResized, this);
31141         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31142         layout.on("regionexpanded", this.onRegionExpanded, this);
31143     },
31144     
31145     storeState : function(){
31146         this.provider.set(this.layout.id+"-layout-state", this.state);
31147     },
31148     
31149     onRegionResized : function(region, newSize){
31150         this.state[region.getPosition()].size = newSize;
31151         this.storeState();
31152     },
31153     
31154     onRegionCollapsed : function(region){
31155         this.state[region.getPosition()].collapsed = true;
31156         this.storeState();
31157     },
31158     
31159     onRegionExpanded : function(region){
31160         this.state[region.getPosition()].collapsed = false;
31161         this.storeState();
31162     }
31163 };/*
31164  * Based on:
31165  * Ext JS Library 1.1.1
31166  * Copyright(c) 2006-2007, Ext JS, LLC.
31167  *
31168  * Originally Released Under LGPL - original licence link has changed is not relivant.
31169  *
31170  * Fork - LGPL
31171  * <script type="text/javascript">
31172  */
31173 /**
31174  * @class Roo.ContentPanel
31175  * @extends Roo.util.Observable
31176  * A basic ContentPanel element.
31177  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31178  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31179  * @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
31180  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31181  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31182  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31183  * @cfg {Toolbar}   toolbar       A toolbar for this panel
31184  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31185  * @cfg {String} title          The title for this panel
31186  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31187  * @cfg {String} url            Calls {@link #setUrl} with this value
31188  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31189  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31190  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31191  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31192
31193  * @constructor
31194  * Create a new ContentPanel.
31195  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31196  * @param {String/Object} config A string to set only the title or a config object
31197  * @param {String} content (optional) Set the HTML content for this panel
31198  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31199  */
31200 Roo.ContentPanel = function(el, config, content){
31201     
31202      
31203     /*
31204     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31205         config = el;
31206         el = Roo.id();
31207     }
31208     if (config && config.parentLayout) { 
31209         el = config.parentLayout.el.createChild(); 
31210     }
31211     */
31212     if(el.autoCreate){ // xtype is available if this is called from factory
31213         config = el;
31214         el = Roo.id();
31215     }
31216     this.el = Roo.get(el);
31217     if(!this.el && config && config.autoCreate){
31218         if(typeof config.autoCreate == "object"){
31219             if(!config.autoCreate.id){
31220                 config.autoCreate.id = config.id||el;
31221             }
31222             this.el = Roo.DomHelper.append(document.body,
31223                         config.autoCreate, true);
31224         }else{
31225             this.el = Roo.DomHelper.append(document.body,
31226                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31227         }
31228     }
31229     this.closable = false;
31230     this.loaded = false;
31231     this.active = false;
31232     if(typeof config == "string"){
31233         this.title = config;
31234     }else{
31235         Roo.apply(this, config);
31236     }
31237     
31238     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31239         this.wrapEl = this.el.wrap();
31240         this.toolbar.container = this.el.insertSibling(false, 'before');
31241         this.toolbar = new Roo.Toolbar(this.toolbar);
31242     }
31243     
31244     // xtype created footer. - not sure if will work as we normally have to render first..
31245     if (this.footer && !this.footer.el && this.footer.xtype) {
31246         if (!this.wrapEl) {
31247             this.wrapEl = this.el.wrap();
31248         }
31249     
31250         this.footer.container = this.wrapEl.createChild();
31251          
31252         this.footer = Roo.factory(this.footer, Roo);
31253         
31254     }
31255     
31256     if(this.resizeEl){
31257         this.resizeEl = Roo.get(this.resizeEl, true);
31258     }else{
31259         this.resizeEl = this.el;
31260     }
31261     // handle view.xtype
31262     
31263  
31264     
31265     
31266     this.addEvents({
31267         /**
31268          * @event activate
31269          * Fires when this panel is activated. 
31270          * @param {Roo.ContentPanel} this
31271          */
31272         "activate" : true,
31273         /**
31274          * @event deactivate
31275          * Fires when this panel is activated. 
31276          * @param {Roo.ContentPanel} this
31277          */
31278         "deactivate" : true,
31279
31280         /**
31281          * @event resize
31282          * Fires when this panel is resized if fitToFrame is true.
31283          * @param {Roo.ContentPanel} this
31284          * @param {Number} width The width after any component adjustments
31285          * @param {Number} height The height after any component adjustments
31286          */
31287         "resize" : true,
31288         
31289          /**
31290          * @event render
31291          * Fires when this tab is created
31292          * @param {Roo.ContentPanel} this
31293          */
31294         "render" : true
31295          
31296         
31297     });
31298     
31299
31300     
31301     
31302     if(this.autoScroll){
31303         this.resizeEl.setStyle("overflow", "auto");
31304     } else {
31305         // fix randome scrolling
31306         this.el.on('scroll', function() {
31307             Roo.log('fix random scolling');
31308             this.scrollTo('top',0); 
31309         });
31310     }
31311     content = content || this.content;
31312     if(content){
31313         this.setContent(content);
31314     }
31315     if(config && config.url){
31316         this.setUrl(this.url, this.params, this.loadOnce);
31317     }
31318     
31319     
31320     
31321     Roo.ContentPanel.superclass.constructor.call(this);
31322     
31323     if (this.view && typeof(this.view.xtype) != 'undefined') {
31324         this.view.el = this.el.appendChild(document.createElement("div"));
31325         this.view = Roo.factory(this.view); 
31326         this.view.render  &&  this.view.render(false, '');  
31327     }
31328     
31329     
31330     this.fireEvent('render', this);
31331 };
31332
31333 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31334     tabTip:'',
31335     setRegion : function(region){
31336         this.region = region;
31337         if(region){
31338            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31339         }else{
31340            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31341         } 
31342     },
31343     
31344     /**
31345      * Returns the toolbar for this Panel if one was configured. 
31346      * @return {Roo.Toolbar} 
31347      */
31348     getToolbar : function(){
31349         return this.toolbar;
31350     },
31351     
31352     setActiveState : function(active){
31353         this.active = active;
31354         if(!active){
31355             this.fireEvent("deactivate", this);
31356         }else{
31357             this.fireEvent("activate", this);
31358         }
31359     },
31360     /**
31361      * Updates this panel's element
31362      * @param {String} content The new content
31363      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31364     */
31365     setContent : function(content, loadScripts){
31366         this.el.update(content, loadScripts);
31367     },
31368
31369     ignoreResize : function(w, h){
31370         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31371             return true;
31372         }else{
31373             this.lastSize = {width: w, height: h};
31374             return false;
31375         }
31376     },
31377     /**
31378      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31379      * @return {Roo.UpdateManager} The UpdateManager
31380      */
31381     getUpdateManager : function(){
31382         return this.el.getUpdateManager();
31383     },
31384      /**
31385      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31386      * @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:
31387 <pre><code>
31388 panel.load({
31389     url: "your-url.php",
31390     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31391     callback: yourFunction,
31392     scope: yourObject, //(optional scope)
31393     discardUrl: false,
31394     nocache: false,
31395     text: "Loading...",
31396     timeout: 30,
31397     scripts: false
31398 });
31399 </code></pre>
31400      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31401      * 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.
31402      * @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}
31403      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31404      * @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.
31405      * @return {Roo.ContentPanel} this
31406      */
31407     load : function(){
31408         var um = this.el.getUpdateManager();
31409         um.update.apply(um, arguments);
31410         return this;
31411     },
31412
31413
31414     /**
31415      * 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.
31416      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31417      * @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)
31418      * @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)
31419      * @return {Roo.UpdateManager} The UpdateManager
31420      */
31421     setUrl : function(url, params, loadOnce){
31422         if(this.refreshDelegate){
31423             this.removeListener("activate", this.refreshDelegate);
31424         }
31425         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31426         this.on("activate", this.refreshDelegate);
31427         return this.el.getUpdateManager();
31428     },
31429     
31430     _handleRefresh : function(url, params, loadOnce){
31431         if(!loadOnce || !this.loaded){
31432             var updater = this.el.getUpdateManager();
31433             updater.update(url, params, this._setLoaded.createDelegate(this));
31434         }
31435     },
31436     
31437     _setLoaded : function(){
31438         this.loaded = true;
31439     }, 
31440     
31441     /**
31442      * Returns this panel's id
31443      * @return {String} 
31444      */
31445     getId : function(){
31446         return this.el.id;
31447     },
31448     
31449     /** 
31450      * Returns this panel's element - used by regiosn to add.
31451      * @return {Roo.Element} 
31452      */
31453     getEl : function(){
31454         return this.wrapEl || this.el;
31455     },
31456     
31457     adjustForComponents : function(width, height)
31458     {
31459         //Roo.log('adjustForComponents ');
31460         if(this.resizeEl != this.el){
31461             width -= this.el.getFrameWidth('lr');
31462             height -= this.el.getFrameWidth('tb');
31463         }
31464         if(this.toolbar){
31465             var te = this.toolbar.getEl();
31466             height -= te.getHeight();
31467             te.setWidth(width);
31468         }
31469         if(this.footer){
31470             var te = this.footer.getEl();
31471             //Roo.log("footer:" + te.getHeight());
31472             
31473             height -= te.getHeight();
31474             te.setWidth(width);
31475         }
31476         
31477         
31478         if(this.adjustments){
31479             width += this.adjustments[0];
31480             height += this.adjustments[1];
31481         }
31482         return {"width": width, "height": height};
31483     },
31484     
31485     setSize : function(width, height){
31486         if(this.fitToFrame && !this.ignoreResize(width, height)){
31487             if(this.fitContainer && this.resizeEl != this.el){
31488                 this.el.setSize(width, height);
31489             }
31490             var size = this.adjustForComponents(width, height);
31491             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31492             this.fireEvent('resize', this, size.width, size.height);
31493         }
31494     },
31495     
31496     /**
31497      * Returns this panel's title
31498      * @return {String} 
31499      */
31500     getTitle : function(){
31501         return this.title;
31502     },
31503     
31504     /**
31505      * Set this panel's title
31506      * @param {String} title
31507      */
31508     setTitle : function(title){
31509         this.title = title;
31510         if(this.region){
31511             this.region.updatePanelTitle(this, title);
31512         }
31513     },
31514     
31515     /**
31516      * Returns true is this panel was configured to be closable
31517      * @return {Boolean} 
31518      */
31519     isClosable : function(){
31520         return this.closable;
31521     },
31522     
31523     beforeSlide : function(){
31524         this.el.clip();
31525         this.resizeEl.clip();
31526     },
31527     
31528     afterSlide : function(){
31529         this.el.unclip();
31530         this.resizeEl.unclip();
31531     },
31532     
31533     /**
31534      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31535      *   Will fail silently if the {@link #setUrl} method has not been called.
31536      *   This does not activate the panel, just updates its content.
31537      */
31538     refresh : function(){
31539         if(this.refreshDelegate){
31540            this.loaded = false;
31541            this.refreshDelegate();
31542         }
31543     },
31544     
31545     /**
31546      * Destroys this panel
31547      */
31548     destroy : function(){
31549         this.el.removeAllListeners();
31550         var tempEl = document.createElement("span");
31551         tempEl.appendChild(this.el.dom);
31552         tempEl.innerHTML = "";
31553         this.el.remove();
31554         this.el = null;
31555     },
31556     
31557     /**
31558      * form - if the content panel contains a form - this is a reference to it.
31559      * @type {Roo.form.Form}
31560      */
31561     form : false,
31562     /**
31563      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31564      *    This contains a reference to it.
31565      * @type {Roo.View}
31566      */
31567     view : false,
31568     
31569       /**
31570      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31571      * <pre><code>
31572
31573 layout.addxtype({
31574        xtype : 'Form',
31575        items: [ .... ]
31576    }
31577 );
31578
31579 </code></pre>
31580      * @param {Object} cfg Xtype definition of item to add.
31581      */
31582     
31583     addxtype : function(cfg) {
31584         // add form..
31585         if (cfg.xtype.match(/^Form$/)) {
31586             
31587             var el;
31588             //if (this.footer) {
31589             //    el = this.footer.container.insertSibling(false, 'before');
31590             //} else {
31591                 el = this.el.createChild();
31592             //}
31593
31594             this.form = new  Roo.form.Form(cfg);
31595             
31596             
31597             if ( this.form.allItems.length) {
31598                 this.form.render(el.dom);
31599             }
31600             return this.form;
31601         }
31602         // should only have one of theses..
31603         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31604             // views.. should not be just added - used named prop 'view''
31605             
31606             cfg.el = this.el.appendChild(document.createElement("div"));
31607             // factory?
31608             
31609             var ret = new Roo.factory(cfg);
31610              
31611              ret.render && ret.render(false, ''); // render blank..
31612             this.view = ret;
31613             return ret;
31614         }
31615         return false;
31616     }
31617 });
31618
31619 /**
31620  * @class Roo.GridPanel
31621  * @extends Roo.ContentPanel
31622  * @constructor
31623  * Create a new GridPanel.
31624  * @param {Roo.grid.Grid} grid The grid for this panel
31625  * @param {String/Object} config A string to set only the panel's title, or a config object
31626  */
31627 Roo.GridPanel = function(grid, config){
31628     
31629   
31630     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31631         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31632         
31633     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31634     
31635     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31636     
31637     if(this.toolbar){
31638         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31639     }
31640     // xtype created footer. - not sure if will work as we normally have to render first..
31641     if (this.footer && !this.footer.el && this.footer.xtype) {
31642         
31643         this.footer.container = this.grid.getView().getFooterPanel(true);
31644         this.footer.dataSource = this.grid.dataSource;
31645         this.footer = Roo.factory(this.footer, Roo);
31646         
31647     }
31648     
31649     grid.monitorWindowResize = false; // turn off autosizing
31650     grid.autoHeight = false;
31651     grid.autoWidth = false;
31652     this.grid = grid;
31653     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31654 };
31655
31656 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31657     getId : function(){
31658         return this.grid.id;
31659     },
31660     
31661     /**
31662      * Returns the grid for this panel
31663      * @return {Roo.grid.Grid} 
31664      */
31665     getGrid : function(){
31666         return this.grid;    
31667     },
31668     
31669     setSize : function(width, height){
31670         if(!this.ignoreResize(width, height)){
31671             var grid = this.grid;
31672             var size = this.adjustForComponents(width, height);
31673             grid.getGridEl().setSize(size.width, size.height);
31674             grid.autoSize();
31675         }
31676     },
31677     
31678     beforeSlide : function(){
31679         this.grid.getView().scroller.clip();
31680     },
31681     
31682     afterSlide : function(){
31683         this.grid.getView().scroller.unclip();
31684     },
31685     
31686     destroy : function(){
31687         this.grid.destroy();
31688         delete this.grid;
31689         Roo.GridPanel.superclass.destroy.call(this); 
31690     }
31691 });
31692
31693
31694 /**
31695  * @class Roo.NestedLayoutPanel
31696  * @extends Roo.ContentPanel
31697  * @constructor
31698  * Create a new NestedLayoutPanel.
31699  * 
31700  * 
31701  * @param {Roo.BorderLayout} layout The layout for this panel
31702  * @param {String/Object} config A string to set only the title or a config object
31703  */
31704 Roo.NestedLayoutPanel = function(layout, config)
31705 {
31706     // construct with only one argument..
31707     /* FIXME - implement nicer consturctors
31708     if (layout.layout) {
31709         config = layout;
31710         layout = config.layout;
31711         delete config.layout;
31712     }
31713     if (layout.xtype && !layout.getEl) {
31714         // then layout needs constructing..
31715         layout = Roo.factory(layout, Roo);
31716     }
31717     */
31718     
31719     
31720     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31721     
31722     layout.monitorWindowResize = false; // turn off autosizing
31723     this.layout = layout;
31724     this.layout.getEl().addClass("x-layout-nested-layout");
31725     
31726     
31727     
31728     
31729 };
31730
31731 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31732
31733     setSize : function(width, height){
31734         if(!this.ignoreResize(width, height)){
31735             var size = this.adjustForComponents(width, height);
31736             var el = this.layout.getEl();
31737             el.setSize(size.width, size.height);
31738             var touch = el.dom.offsetWidth;
31739             this.layout.layout();
31740             // ie requires a double layout on the first pass
31741             if(Roo.isIE && !this.initialized){
31742                 this.initialized = true;
31743                 this.layout.layout();
31744             }
31745         }
31746     },
31747     
31748     // activate all subpanels if not currently active..
31749     
31750     setActiveState : function(active){
31751         this.active = active;
31752         if(!active){
31753             this.fireEvent("deactivate", this);
31754             return;
31755         }
31756         
31757         this.fireEvent("activate", this);
31758         // not sure if this should happen before or after..
31759         if (!this.layout) {
31760             return; // should not happen..
31761         }
31762         var reg = false;
31763         for (var r in this.layout.regions) {
31764             reg = this.layout.getRegion(r);
31765             if (reg.getActivePanel()) {
31766                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31767                 reg.setActivePanel(reg.getActivePanel());
31768                 continue;
31769             }
31770             if (!reg.panels.length) {
31771                 continue;
31772             }
31773             reg.showPanel(reg.getPanel(0));
31774         }
31775         
31776         
31777         
31778         
31779     },
31780     
31781     /**
31782      * Returns the nested BorderLayout for this panel
31783      * @return {Roo.BorderLayout} 
31784      */
31785     getLayout : function(){
31786         return this.layout;
31787     },
31788     
31789      /**
31790      * Adds a xtype elements to the layout of the nested panel
31791      * <pre><code>
31792
31793 panel.addxtype({
31794        xtype : 'ContentPanel',
31795        region: 'west',
31796        items: [ .... ]
31797    }
31798 );
31799
31800 panel.addxtype({
31801         xtype : 'NestedLayoutPanel',
31802         region: 'west',
31803         layout: {
31804            center: { },
31805            west: { }   
31806         },
31807         items : [ ... list of content panels or nested layout panels.. ]
31808    }
31809 );
31810 </code></pre>
31811      * @param {Object} cfg Xtype definition of item to add.
31812      */
31813     addxtype : function(cfg) {
31814         return this.layout.addxtype(cfg);
31815     
31816     }
31817 });
31818
31819 Roo.ScrollPanel = function(el, config, content){
31820     config = config || {};
31821     config.fitToFrame = true;
31822     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31823     
31824     this.el.dom.style.overflow = "hidden";
31825     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31826     this.el.removeClass("x-layout-inactive-content");
31827     this.el.on("mousewheel", this.onWheel, this);
31828
31829     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31830     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31831     up.unselectable(); down.unselectable();
31832     up.on("click", this.scrollUp, this);
31833     down.on("click", this.scrollDown, this);
31834     up.addClassOnOver("x-scroller-btn-over");
31835     down.addClassOnOver("x-scroller-btn-over");
31836     up.addClassOnClick("x-scroller-btn-click");
31837     down.addClassOnClick("x-scroller-btn-click");
31838     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31839
31840     this.resizeEl = this.el;
31841     this.el = wrap; this.up = up; this.down = down;
31842 };
31843
31844 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31845     increment : 100,
31846     wheelIncrement : 5,
31847     scrollUp : function(){
31848         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31849     },
31850
31851     scrollDown : function(){
31852         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31853     },
31854
31855     afterScroll : function(){
31856         var el = this.resizeEl;
31857         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31858         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31859         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31860     },
31861
31862     setSize : function(){
31863         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31864         this.afterScroll();
31865     },
31866
31867     onWheel : function(e){
31868         var d = e.getWheelDelta();
31869         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31870         this.afterScroll();
31871         e.stopEvent();
31872     },
31873
31874     setContent : function(content, loadScripts){
31875         this.resizeEl.update(content, loadScripts);
31876     }
31877
31878 });
31879
31880
31881
31882
31883
31884
31885
31886
31887
31888 /**
31889  * @class Roo.TreePanel
31890  * @extends Roo.ContentPanel
31891  * @constructor
31892  * Create a new TreePanel. - defaults to fit/scoll contents.
31893  * @param {String/Object} config A string to set only the panel's title, or a config object
31894  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31895  */
31896 Roo.TreePanel = function(config){
31897     var el = config.el;
31898     var tree = config.tree;
31899     delete config.tree; 
31900     delete config.el; // hopefull!
31901     
31902     // wrapper for IE7 strict & safari scroll issue
31903     
31904     var treeEl = el.createChild();
31905     config.resizeEl = treeEl;
31906     
31907     
31908     
31909     Roo.TreePanel.superclass.constructor.call(this, el, config);
31910  
31911  
31912     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31913     //console.log(tree);
31914     this.on('activate', function()
31915     {
31916         if (this.tree.rendered) {
31917             return;
31918         }
31919         //console.log('render tree');
31920         this.tree.render();
31921     });
31922     // this should not be needed.. - it's actually the 'el' that resizes?
31923     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31924     
31925     //this.on('resize',  function (cp, w, h) {
31926     //        this.tree.innerCt.setWidth(w);
31927     //        this.tree.innerCt.setHeight(h);
31928     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
31929     //});
31930
31931         
31932     
31933 };
31934
31935 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31936     fitToFrame : true,
31937     autoScroll : true
31938 });
31939
31940
31941
31942
31943
31944
31945
31946
31947
31948
31949
31950 /*
31951  * Based on:
31952  * Ext JS Library 1.1.1
31953  * Copyright(c) 2006-2007, Ext JS, LLC.
31954  *
31955  * Originally Released Under LGPL - original licence link has changed is not relivant.
31956  *
31957  * Fork - LGPL
31958  * <script type="text/javascript">
31959  */
31960  
31961
31962 /**
31963  * @class Roo.ReaderLayout
31964  * @extends Roo.BorderLayout
31965  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31966  * center region containing two nested regions (a top one for a list view and one for item preview below),
31967  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31968  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31969  * expedites the setup of the overall layout and regions for this common application style.
31970  * Example:
31971  <pre><code>
31972 var reader = new Roo.ReaderLayout();
31973 var CP = Roo.ContentPanel;  // shortcut for adding
31974
31975 reader.beginUpdate();
31976 reader.add("north", new CP("north", "North"));
31977 reader.add("west", new CP("west", {title: "West"}));
31978 reader.add("east", new CP("east", {title: "East"}));
31979
31980 reader.regions.listView.add(new CP("listView", "List"));
31981 reader.regions.preview.add(new CP("preview", "Preview"));
31982 reader.endUpdate();
31983 </code></pre>
31984 * @constructor
31985 * Create a new ReaderLayout
31986 * @param {Object} config Configuration options
31987 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31988 * document.body if omitted)
31989 */
31990 Roo.ReaderLayout = function(config, renderTo){
31991     var c = config || {size:{}};
31992     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31993         north: c.north !== false ? Roo.apply({
31994             split:false,
31995             initialSize: 32,
31996             titlebar: false
31997         }, c.north) : false,
31998         west: c.west !== false ? Roo.apply({
31999             split:true,
32000             initialSize: 200,
32001             minSize: 175,
32002             maxSize: 400,
32003             titlebar: true,
32004             collapsible: true,
32005             animate: true,
32006             margins:{left:5,right:0,bottom:5,top:5},
32007             cmargins:{left:5,right:5,bottom:5,top:5}
32008         }, c.west) : false,
32009         east: c.east !== false ? Roo.apply({
32010             split:true,
32011             initialSize: 200,
32012             minSize: 175,
32013             maxSize: 400,
32014             titlebar: true,
32015             collapsible: true,
32016             animate: true,
32017             margins:{left:0,right:5,bottom:5,top:5},
32018             cmargins:{left:5,right:5,bottom:5,top:5}
32019         }, c.east) : false,
32020         center: Roo.apply({
32021             tabPosition: 'top',
32022             autoScroll:false,
32023             closeOnTab: true,
32024             titlebar:false,
32025             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32026         }, c.center)
32027     });
32028
32029     this.el.addClass('x-reader');
32030
32031     this.beginUpdate();
32032
32033     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32034         south: c.preview !== false ? Roo.apply({
32035             split:true,
32036             initialSize: 200,
32037             minSize: 100,
32038             autoScroll:true,
32039             collapsible:true,
32040             titlebar: true,
32041             cmargins:{top:5,left:0, right:0, bottom:0}
32042         }, c.preview) : false,
32043         center: Roo.apply({
32044             autoScroll:false,
32045             titlebar:false,
32046             minHeight:200
32047         }, c.listView)
32048     });
32049     this.add('center', new Roo.NestedLayoutPanel(inner,
32050             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32051
32052     this.endUpdate();
32053
32054     this.regions.preview = inner.getRegion('south');
32055     this.regions.listView = inner.getRegion('center');
32056 };
32057
32058 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32059  * Based on:
32060  * Ext JS Library 1.1.1
32061  * Copyright(c) 2006-2007, Ext JS, LLC.
32062  *
32063  * Originally Released Under LGPL - original licence link has changed is not relivant.
32064  *
32065  * Fork - LGPL
32066  * <script type="text/javascript">
32067  */
32068  
32069 /**
32070  * @class Roo.grid.Grid
32071  * @extends Roo.util.Observable
32072  * This class represents the primary interface of a component based grid control.
32073  * <br><br>Usage:<pre><code>
32074  var grid = new Roo.grid.Grid("my-container-id", {
32075      ds: myDataStore,
32076      cm: myColModel,
32077      selModel: mySelectionModel,
32078      autoSizeColumns: true,
32079      monitorWindowResize: false,
32080      trackMouseOver: true
32081  });
32082  // set any options
32083  grid.render();
32084  * </code></pre>
32085  * <b>Common Problems:</b><br/>
32086  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32087  * element will correct this<br/>
32088  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32089  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32090  * are unpredictable.<br/>
32091  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32092  * grid to calculate dimensions/offsets.<br/>
32093   * @constructor
32094  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32095  * The container MUST have some type of size defined for the grid to fill. The container will be
32096  * automatically set to position relative if it isn't already.
32097  * @param {Object} config A config object that sets properties on this grid.
32098  */
32099 Roo.grid.Grid = function(container, config){
32100         // initialize the container
32101         this.container = Roo.get(container);
32102         this.container.update("");
32103         this.container.setStyle("overflow", "hidden");
32104     this.container.addClass('x-grid-container');
32105
32106     this.id = this.container.id;
32107
32108     Roo.apply(this, config);
32109     // check and correct shorthanded configs
32110     if(this.ds){
32111         this.dataSource = this.ds;
32112         delete this.ds;
32113     }
32114     if(this.cm){
32115         this.colModel = this.cm;
32116         delete this.cm;
32117     }
32118     if(this.sm){
32119         this.selModel = this.sm;
32120         delete this.sm;
32121     }
32122
32123     if (this.selModel) {
32124         this.selModel = Roo.factory(this.selModel, Roo.grid);
32125         this.sm = this.selModel;
32126         this.sm.xmodule = this.xmodule || false;
32127     }
32128     if (typeof(this.colModel.config) == 'undefined') {
32129         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32130         this.cm = this.colModel;
32131         this.cm.xmodule = this.xmodule || false;
32132     }
32133     if (this.dataSource) {
32134         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32135         this.ds = this.dataSource;
32136         this.ds.xmodule = this.xmodule || false;
32137          
32138     }
32139     
32140     
32141     
32142     if(this.width){
32143         this.container.setWidth(this.width);
32144     }
32145
32146     if(this.height){
32147         this.container.setHeight(this.height);
32148     }
32149     /** @private */
32150         this.addEvents({
32151         // raw events
32152         /**
32153          * @event click
32154          * The raw click event for the entire grid.
32155          * @param {Roo.EventObject} e
32156          */
32157         "click" : true,
32158         /**
32159          * @event dblclick
32160          * The raw dblclick event for the entire grid.
32161          * @param {Roo.EventObject} e
32162          */
32163         "dblclick" : true,
32164         /**
32165          * @event contextmenu
32166          * The raw contextmenu event for the entire grid.
32167          * @param {Roo.EventObject} e
32168          */
32169         "contextmenu" : true,
32170         /**
32171          * @event mousedown
32172          * The raw mousedown event for the entire grid.
32173          * @param {Roo.EventObject} e
32174          */
32175         "mousedown" : true,
32176         /**
32177          * @event mouseup
32178          * The raw mouseup event for the entire grid.
32179          * @param {Roo.EventObject} e
32180          */
32181         "mouseup" : true,
32182         /**
32183          * @event mouseover
32184          * The raw mouseover event for the entire grid.
32185          * @param {Roo.EventObject} e
32186          */
32187         "mouseover" : true,
32188         /**
32189          * @event mouseout
32190          * The raw mouseout event for the entire grid.
32191          * @param {Roo.EventObject} e
32192          */
32193         "mouseout" : true,
32194         /**
32195          * @event keypress
32196          * The raw keypress event for the entire grid.
32197          * @param {Roo.EventObject} e
32198          */
32199         "keypress" : true,
32200         /**
32201          * @event keydown
32202          * The raw keydown event for the entire grid.
32203          * @param {Roo.EventObject} e
32204          */
32205         "keydown" : true,
32206
32207         // custom events
32208
32209         /**
32210          * @event cellclick
32211          * Fires when a cell is clicked
32212          * @param {Grid} this
32213          * @param {Number} rowIndex
32214          * @param {Number} columnIndex
32215          * @param {Roo.EventObject} e
32216          */
32217         "cellclick" : true,
32218         /**
32219          * @event celldblclick
32220          * Fires when a cell is double clicked
32221          * @param {Grid} this
32222          * @param {Number} rowIndex
32223          * @param {Number} columnIndex
32224          * @param {Roo.EventObject} e
32225          */
32226         "celldblclick" : true,
32227         /**
32228          * @event rowclick
32229          * Fires when a row is clicked
32230          * @param {Grid} this
32231          * @param {Number} rowIndex
32232          * @param {Roo.EventObject} e
32233          */
32234         "rowclick" : true,
32235         /**
32236          * @event rowdblclick
32237          * Fires when a row is double clicked
32238          * @param {Grid} this
32239          * @param {Number} rowIndex
32240          * @param {Roo.EventObject} e
32241          */
32242         "rowdblclick" : true,
32243         /**
32244          * @event headerclick
32245          * Fires when a header is clicked
32246          * @param {Grid} this
32247          * @param {Number} columnIndex
32248          * @param {Roo.EventObject} e
32249          */
32250         "headerclick" : true,
32251         /**
32252          * @event headerdblclick
32253          * Fires when a header cell is double clicked
32254          * @param {Grid} this
32255          * @param {Number} columnIndex
32256          * @param {Roo.EventObject} e
32257          */
32258         "headerdblclick" : true,
32259         /**
32260          * @event rowcontextmenu
32261          * Fires when a row is right clicked
32262          * @param {Grid} this
32263          * @param {Number} rowIndex
32264          * @param {Roo.EventObject} e
32265          */
32266         "rowcontextmenu" : true,
32267         /**
32268          * @event cellcontextmenu
32269          * Fires when a cell is right clicked
32270          * @param {Grid} this
32271          * @param {Number} rowIndex
32272          * @param {Number} cellIndex
32273          * @param {Roo.EventObject} e
32274          */
32275          "cellcontextmenu" : true,
32276         /**
32277          * @event headercontextmenu
32278          * Fires when a header is right clicked
32279          * @param {Grid} this
32280          * @param {Number} columnIndex
32281          * @param {Roo.EventObject} e
32282          */
32283         "headercontextmenu" : true,
32284         /**
32285          * @event bodyscroll
32286          * Fires when the body element is scrolled
32287          * @param {Number} scrollLeft
32288          * @param {Number} scrollTop
32289          */
32290         "bodyscroll" : true,
32291         /**
32292          * @event columnresize
32293          * Fires when the user resizes a column
32294          * @param {Number} columnIndex
32295          * @param {Number} newSize
32296          */
32297         "columnresize" : true,
32298         /**
32299          * @event columnmove
32300          * Fires when the user moves a column
32301          * @param {Number} oldIndex
32302          * @param {Number} newIndex
32303          */
32304         "columnmove" : true,
32305         /**
32306          * @event startdrag
32307          * Fires when row(s) start being dragged
32308          * @param {Grid} this
32309          * @param {Roo.GridDD} dd The drag drop object
32310          * @param {event} e The raw browser event
32311          */
32312         "startdrag" : true,
32313         /**
32314          * @event enddrag
32315          * Fires when a drag operation is complete
32316          * @param {Grid} this
32317          * @param {Roo.GridDD} dd The drag drop object
32318          * @param {event} e The raw browser event
32319          */
32320         "enddrag" : true,
32321         /**
32322          * @event dragdrop
32323          * Fires when dragged row(s) are dropped on a valid DD target
32324          * @param {Grid} this
32325          * @param {Roo.GridDD} dd The drag drop object
32326          * @param {String} targetId The target drag drop object
32327          * @param {event} e The raw browser event
32328          */
32329         "dragdrop" : true,
32330         /**
32331          * @event dragover
32332          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32333          * @param {Grid} this
32334          * @param {Roo.GridDD} dd The drag drop object
32335          * @param {String} targetId The target drag drop object
32336          * @param {event} e The raw browser event
32337          */
32338         "dragover" : true,
32339         /**
32340          * @event dragenter
32341          *  Fires when the dragged row(s) first cross another DD target while being dragged
32342          * @param {Grid} this
32343          * @param {Roo.GridDD} dd The drag drop object
32344          * @param {String} targetId The target drag drop object
32345          * @param {event} e The raw browser event
32346          */
32347         "dragenter" : true,
32348         /**
32349          * @event dragout
32350          * Fires when the dragged row(s) leave another DD target while being dragged
32351          * @param {Grid} this
32352          * @param {Roo.GridDD} dd The drag drop object
32353          * @param {String} targetId The target drag drop object
32354          * @param {event} e The raw browser event
32355          */
32356         "dragout" : true,
32357         /**
32358          * @event rowclass
32359          * Fires when a row is rendered, so you can change add a style to it.
32360          * @param {GridView} gridview   The grid view
32361          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32362          */
32363         'rowclass' : true,
32364
32365         /**
32366          * @event render
32367          * Fires when the grid is rendered
32368          * @param {Grid} grid
32369          */
32370         'render' : true
32371     });
32372
32373     Roo.grid.Grid.superclass.constructor.call(this);
32374 };
32375 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32376     
32377     /**
32378      * @cfg {String} ddGroup - drag drop group.
32379      */
32380
32381     /**
32382      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32383      */
32384     minColumnWidth : 25,
32385
32386     /**
32387      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32388      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32389      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32390      */
32391     autoSizeColumns : false,
32392
32393     /**
32394      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32395      */
32396     autoSizeHeaders : true,
32397
32398     /**
32399      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32400      */
32401     monitorWindowResize : true,
32402
32403     /**
32404      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32405      * rows measured to get a columns size. Default is 0 (all rows).
32406      */
32407     maxRowsToMeasure : 0,
32408
32409     /**
32410      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32411      */
32412     trackMouseOver : true,
32413
32414     /**
32415     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32416     */
32417     
32418     /**
32419     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32420     */
32421     enableDragDrop : false,
32422     
32423     /**
32424     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32425     */
32426     enableColumnMove : true,
32427     
32428     /**
32429     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32430     */
32431     enableColumnHide : true,
32432     
32433     /**
32434     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32435     */
32436     enableRowHeightSync : false,
32437     
32438     /**
32439     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32440     */
32441     stripeRows : true,
32442     
32443     /**
32444     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32445     */
32446     autoHeight : false,
32447
32448     /**
32449      * @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.
32450      */
32451     autoExpandColumn : false,
32452
32453     /**
32454     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32455     * Default is 50.
32456     */
32457     autoExpandMin : 50,
32458
32459     /**
32460     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32461     */
32462     autoExpandMax : 1000,
32463
32464     /**
32465     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32466     */
32467     view : null,
32468
32469     /**
32470     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32471     */
32472     loadMask : false,
32473     /**
32474     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32475     */
32476     dropTarget: false,
32477     
32478    
32479     
32480     // private
32481     rendered : false,
32482
32483     /**
32484     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32485     * of a fixed width. Default is false.
32486     */
32487     /**
32488     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32489     */
32490     /**
32491      * Called once after all setup has been completed and the grid is ready to be rendered.
32492      * @return {Roo.grid.Grid} this
32493      */
32494     render : function()
32495     {
32496         var c = this.container;
32497         // try to detect autoHeight/width mode
32498         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32499             this.autoHeight = true;
32500         }
32501         var view = this.getView();
32502         view.init(this);
32503
32504         c.on("click", this.onClick, this);
32505         c.on("dblclick", this.onDblClick, this);
32506         c.on("contextmenu", this.onContextMenu, this);
32507         c.on("keydown", this.onKeyDown, this);
32508         if (Roo.isTouch) {
32509             c.on("touchstart", this.onTouchStart, this);
32510         }
32511
32512         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32513
32514         this.getSelectionModel().init(this);
32515
32516         view.render();
32517
32518         if(this.loadMask){
32519             this.loadMask = new Roo.LoadMask(this.container,
32520                     Roo.apply({store:this.dataSource}, this.loadMask));
32521         }
32522         
32523         
32524         if (this.toolbar && this.toolbar.xtype) {
32525             this.toolbar.container = this.getView().getHeaderPanel(true);
32526             this.toolbar = new Roo.Toolbar(this.toolbar);
32527         }
32528         if (this.footer && this.footer.xtype) {
32529             this.footer.dataSource = this.getDataSource();
32530             this.footer.container = this.getView().getFooterPanel(true);
32531             this.footer = Roo.factory(this.footer, Roo);
32532         }
32533         if (this.dropTarget && this.dropTarget.xtype) {
32534             delete this.dropTarget.xtype;
32535             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32536         }
32537         
32538         
32539         this.rendered = true;
32540         this.fireEvent('render', this);
32541         return this;
32542     },
32543
32544     /**
32545      * Reconfigures the grid to use a different Store and Column Model.
32546      * The View will be bound to the new objects and refreshed.
32547      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32548      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32549      */
32550     reconfigure : function(dataSource, colModel){
32551         if(this.loadMask){
32552             this.loadMask.destroy();
32553             this.loadMask = new Roo.LoadMask(this.container,
32554                     Roo.apply({store:dataSource}, this.loadMask));
32555         }
32556         this.view.bind(dataSource, colModel);
32557         this.dataSource = dataSource;
32558         this.colModel = colModel;
32559         this.view.refresh(true);
32560     },
32561     /**
32562      * addColumns
32563      * Add's a column, default at the end..
32564      
32565      * @param {int} position to add (default end)
32566      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
32567      */
32568     addColumns : function(pos, ar)
32569     {
32570         
32571         for (var i =0;i< ar.length;i++) {
32572             var cfg = ar[i];
32573             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
32574             this.cm.lookup[cfg.id] = cfg;
32575         }
32576         
32577         
32578         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
32579             pos = this.cm.config.length; //this.cm.config.push(cfg);
32580         } 
32581         pos = Math.max(0,pos);
32582         ar.unshift(0);
32583         ar.unshift(pos);
32584         this.cm.config.splice.apply(this.cm.config, ar);
32585         
32586         
32587         
32588         this.view.generateRules(this.cm);
32589         this.view.refresh(true);
32590         
32591     },
32592     
32593     
32594     
32595     
32596     // private
32597     onKeyDown : function(e){
32598         this.fireEvent("keydown", e);
32599     },
32600
32601     /**
32602      * Destroy this grid.
32603      * @param {Boolean} removeEl True to remove the element
32604      */
32605     destroy : function(removeEl, keepListeners){
32606         if(this.loadMask){
32607             this.loadMask.destroy();
32608         }
32609         var c = this.container;
32610         c.removeAllListeners();
32611         this.view.destroy();
32612         this.colModel.purgeListeners();
32613         if(!keepListeners){
32614             this.purgeListeners();
32615         }
32616         c.update("");
32617         if(removeEl === true){
32618             c.remove();
32619         }
32620     },
32621
32622     // private
32623     processEvent : function(name, e){
32624         // does this fire select???
32625         //Roo.log('grid:processEvent '  + name);
32626         
32627         if (name != 'touchstart' ) {
32628             this.fireEvent(name, e);    
32629         }
32630         
32631         var t = e.getTarget();
32632         var v = this.view;
32633         var header = v.findHeaderIndex(t);
32634         if(header !== false){
32635             var ename = name == 'touchstart' ? 'click' : name;
32636              
32637             this.fireEvent("header" + ename, this, header, e);
32638         }else{
32639             var row = v.findRowIndex(t);
32640             var cell = v.findCellIndex(t);
32641             if (name == 'touchstart') {
32642                 // first touch is always a click.
32643                 // hopefull this happens after selection is updated.?
32644                 name = false;
32645                 
32646                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32647                     var cs = this.selModel.getSelectedCell();
32648                     if (row == cs[0] && cell == cs[1]){
32649                         name = 'dblclick';
32650                     }
32651                 }
32652                 if (typeof(this.selModel.getSelections) != 'undefined') {
32653                     var cs = this.selModel.getSelections();
32654                     var ds = this.dataSource;
32655                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32656                         name = 'dblclick';
32657                     }
32658                 }
32659                 if (!name) {
32660                     return;
32661                 }
32662             }
32663             
32664             
32665             if(row !== false){
32666                 this.fireEvent("row" + name, this, row, e);
32667                 if(cell !== false){
32668                     this.fireEvent("cell" + name, this, row, cell, e);
32669                 }
32670             }
32671         }
32672     },
32673
32674     // private
32675     onClick : function(e){
32676         this.processEvent("click", e);
32677     },
32678    // private
32679     onTouchStart : function(e){
32680         this.processEvent("touchstart", e);
32681     },
32682
32683     // private
32684     onContextMenu : function(e, t){
32685         this.processEvent("contextmenu", e);
32686     },
32687
32688     // private
32689     onDblClick : function(e){
32690         this.processEvent("dblclick", e);
32691     },
32692
32693     // private
32694     walkCells : function(row, col, step, fn, scope){
32695         var cm = this.colModel, clen = cm.getColumnCount();
32696         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32697         if(step < 0){
32698             if(col < 0){
32699                 row--;
32700                 first = false;
32701             }
32702             while(row >= 0){
32703                 if(!first){
32704                     col = clen-1;
32705                 }
32706                 first = false;
32707                 while(col >= 0){
32708                     if(fn.call(scope || this, row, col, cm) === true){
32709                         return [row, col];
32710                     }
32711                     col--;
32712                 }
32713                 row--;
32714             }
32715         } else {
32716             if(col >= clen){
32717                 row++;
32718                 first = false;
32719             }
32720             while(row < rlen){
32721                 if(!first){
32722                     col = 0;
32723                 }
32724                 first = false;
32725                 while(col < clen){
32726                     if(fn.call(scope || this, row, col, cm) === true){
32727                         return [row, col];
32728                     }
32729                     col++;
32730                 }
32731                 row++;
32732             }
32733         }
32734         return null;
32735     },
32736
32737     // private
32738     getSelections : function(){
32739         return this.selModel.getSelections();
32740     },
32741
32742     /**
32743      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32744      * but if manual update is required this method will initiate it.
32745      */
32746     autoSize : function(){
32747         if(this.rendered){
32748             this.view.layout();
32749             if(this.view.adjustForScroll){
32750                 this.view.adjustForScroll();
32751             }
32752         }
32753     },
32754
32755     /**
32756      * Returns the grid's underlying element.
32757      * @return {Element} The element
32758      */
32759     getGridEl : function(){
32760         return this.container;
32761     },
32762
32763     // private for compatibility, overridden by editor grid
32764     stopEditing : function(){},
32765
32766     /**
32767      * Returns the grid's SelectionModel.
32768      * @return {SelectionModel}
32769      */
32770     getSelectionModel : function(){
32771         if(!this.selModel){
32772             this.selModel = new Roo.grid.RowSelectionModel();
32773         }
32774         return this.selModel;
32775     },
32776
32777     /**
32778      * Returns the grid's DataSource.
32779      * @return {DataSource}
32780      */
32781     getDataSource : function(){
32782         return this.dataSource;
32783     },
32784
32785     /**
32786      * Returns the grid's ColumnModel.
32787      * @return {ColumnModel}
32788      */
32789     getColumnModel : function(){
32790         return this.colModel;
32791     },
32792
32793     /**
32794      * Returns the grid's GridView object.
32795      * @return {GridView}
32796      */
32797     getView : function(){
32798         if(!this.view){
32799             this.view = new Roo.grid.GridView(this.viewConfig);
32800         }
32801         return this.view;
32802     },
32803     /**
32804      * Called to get grid's drag proxy text, by default returns this.ddText.
32805      * @return {String}
32806      */
32807     getDragDropText : function(){
32808         var count = this.selModel.getCount();
32809         return String.format(this.ddText, count, count == 1 ? '' : 's');
32810     }
32811 });
32812 /**
32813  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32814  * %0 is replaced with the number of selected rows.
32815  * @type String
32816  */
32817 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32818  * Based on:
32819  * Ext JS Library 1.1.1
32820  * Copyright(c) 2006-2007, Ext JS, LLC.
32821  *
32822  * Originally Released Under LGPL - original licence link has changed is not relivant.
32823  *
32824  * Fork - LGPL
32825  * <script type="text/javascript">
32826  */
32827  
32828 Roo.grid.AbstractGridView = function(){
32829         this.grid = null;
32830         
32831         this.events = {
32832             "beforerowremoved" : true,
32833             "beforerowsinserted" : true,
32834             "beforerefresh" : true,
32835             "rowremoved" : true,
32836             "rowsinserted" : true,
32837             "rowupdated" : true,
32838             "refresh" : true
32839         };
32840     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32841 };
32842
32843 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32844     rowClass : "x-grid-row",
32845     cellClass : "x-grid-cell",
32846     tdClass : "x-grid-td",
32847     hdClass : "x-grid-hd",
32848     splitClass : "x-grid-hd-split",
32849     
32850     init: function(grid){
32851         this.grid = grid;
32852                 var cid = this.grid.getGridEl().id;
32853         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32854         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32855         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32856         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32857         },
32858         
32859     getColumnRenderers : function(){
32860         var renderers = [];
32861         var cm = this.grid.colModel;
32862         var colCount = cm.getColumnCount();
32863         for(var i = 0; i < colCount; i++){
32864             renderers[i] = cm.getRenderer(i);
32865         }
32866         return renderers;
32867     },
32868     
32869     getColumnIds : function(){
32870         var ids = [];
32871         var cm = this.grid.colModel;
32872         var colCount = cm.getColumnCount();
32873         for(var i = 0; i < colCount; i++){
32874             ids[i] = cm.getColumnId(i);
32875         }
32876         return ids;
32877     },
32878     
32879     getDataIndexes : function(){
32880         if(!this.indexMap){
32881             this.indexMap = this.buildIndexMap();
32882         }
32883         return this.indexMap.colToData;
32884     },
32885     
32886     getColumnIndexByDataIndex : function(dataIndex){
32887         if(!this.indexMap){
32888             this.indexMap = this.buildIndexMap();
32889         }
32890         return this.indexMap.dataToCol[dataIndex];
32891     },
32892     
32893     /**
32894      * Set a css style for a column dynamically. 
32895      * @param {Number} colIndex The index of the column
32896      * @param {String} name The css property name
32897      * @param {String} value The css value
32898      */
32899     setCSSStyle : function(colIndex, name, value){
32900         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32901         Roo.util.CSS.updateRule(selector, name, value);
32902     },
32903     
32904     generateRules : function(cm){
32905         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32906         Roo.util.CSS.removeStyleSheet(rulesId);
32907         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32908             var cid = cm.getColumnId(i);
32909             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32910                          this.tdSelector, cid, " {\n}\n",
32911                          this.hdSelector, cid, " {\n}\n",
32912                          this.splitSelector, cid, " {\n}\n");
32913         }
32914         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32915     }
32916 });/*
32917  * Based on:
32918  * Ext JS Library 1.1.1
32919  * Copyright(c) 2006-2007, Ext JS, LLC.
32920  *
32921  * Originally Released Under LGPL - original licence link has changed is not relivant.
32922  *
32923  * Fork - LGPL
32924  * <script type="text/javascript">
32925  */
32926
32927 // private
32928 // This is a support class used internally by the Grid components
32929 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32930     this.grid = grid;
32931     this.view = grid.getView();
32932     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32933     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32934     if(hd2){
32935         this.setHandleElId(Roo.id(hd));
32936         this.setOuterHandleElId(Roo.id(hd2));
32937     }
32938     this.scroll = false;
32939 };
32940 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32941     maxDragWidth: 120,
32942     getDragData : function(e){
32943         var t = Roo.lib.Event.getTarget(e);
32944         var h = this.view.findHeaderCell(t);
32945         if(h){
32946             return {ddel: h.firstChild, header:h};
32947         }
32948         return false;
32949     },
32950
32951     onInitDrag : function(e){
32952         this.view.headersDisabled = true;
32953         var clone = this.dragData.ddel.cloneNode(true);
32954         clone.id = Roo.id();
32955         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32956         this.proxy.update(clone);
32957         return true;
32958     },
32959
32960     afterValidDrop : function(){
32961         var v = this.view;
32962         setTimeout(function(){
32963             v.headersDisabled = false;
32964         }, 50);
32965     },
32966
32967     afterInvalidDrop : function(){
32968         var v = this.view;
32969         setTimeout(function(){
32970             v.headersDisabled = false;
32971         }, 50);
32972     }
32973 });
32974 /*
32975  * Based on:
32976  * Ext JS Library 1.1.1
32977  * Copyright(c) 2006-2007, Ext JS, LLC.
32978  *
32979  * Originally Released Under LGPL - original licence link has changed is not relivant.
32980  *
32981  * Fork - LGPL
32982  * <script type="text/javascript">
32983  */
32984 // private
32985 // This is a support class used internally by the Grid components
32986 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32987     this.grid = grid;
32988     this.view = grid.getView();
32989     // split the proxies so they don't interfere with mouse events
32990     this.proxyTop = Roo.DomHelper.append(document.body, {
32991         cls:"col-move-top", html:"&#160;"
32992     }, true);
32993     this.proxyBottom = Roo.DomHelper.append(document.body, {
32994         cls:"col-move-bottom", html:"&#160;"
32995     }, true);
32996     this.proxyTop.hide = this.proxyBottom.hide = function(){
32997         this.setLeftTop(-100,-100);
32998         this.setStyle("visibility", "hidden");
32999     };
33000     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33001     // temporarily disabled
33002     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33003     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33004 };
33005 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33006     proxyOffsets : [-4, -9],
33007     fly: Roo.Element.fly,
33008
33009     getTargetFromEvent : function(e){
33010         var t = Roo.lib.Event.getTarget(e);
33011         var cindex = this.view.findCellIndex(t);
33012         if(cindex !== false){
33013             return this.view.getHeaderCell(cindex);
33014         }
33015         return null;
33016     },
33017
33018     nextVisible : function(h){
33019         var v = this.view, cm = this.grid.colModel;
33020         h = h.nextSibling;
33021         while(h){
33022             if(!cm.isHidden(v.getCellIndex(h))){
33023                 return h;
33024             }
33025             h = h.nextSibling;
33026         }
33027         return null;
33028     },
33029
33030     prevVisible : function(h){
33031         var v = this.view, cm = this.grid.colModel;
33032         h = h.prevSibling;
33033         while(h){
33034             if(!cm.isHidden(v.getCellIndex(h))){
33035                 return h;
33036             }
33037             h = h.prevSibling;
33038         }
33039         return null;
33040     },
33041
33042     positionIndicator : function(h, n, e){
33043         var x = Roo.lib.Event.getPageX(e);
33044         var r = Roo.lib.Dom.getRegion(n.firstChild);
33045         var px, pt, py = r.top + this.proxyOffsets[1];
33046         if((r.right - x) <= (r.right-r.left)/2){
33047             px = r.right+this.view.borderWidth;
33048             pt = "after";
33049         }else{
33050             px = r.left;
33051             pt = "before";
33052         }
33053         var oldIndex = this.view.getCellIndex(h);
33054         var newIndex = this.view.getCellIndex(n);
33055
33056         if(this.grid.colModel.isFixed(newIndex)){
33057             return false;
33058         }
33059
33060         var locked = this.grid.colModel.isLocked(newIndex);
33061
33062         if(pt == "after"){
33063             newIndex++;
33064         }
33065         if(oldIndex < newIndex){
33066             newIndex--;
33067         }
33068         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33069             return false;
33070         }
33071         px +=  this.proxyOffsets[0];
33072         this.proxyTop.setLeftTop(px, py);
33073         this.proxyTop.show();
33074         if(!this.bottomOffset){
33075             this.bottomOffset = this.view.mainHd.getHeight();
33076         }
33077         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33078         this.proxyBottom.show();
33079         return pt;
33080     },
33081
33082     onNodeEnter : function(n, dd, e, data){
33083         if(data.header != n){
33084             this.positionIndicator(data.header, n, e);
33085         }
33086     },
33087
33088     onNodeOver : function(n, dd, e, data){
33089         var result = false;
33090         if(data.header != n){
33091             result = this.positionIndicator(data.header, n, e);
33092         }
33093         if(!result){
33094             this.proxyTop.hide();
33095             this.proxyBottom.hide();
33096         }
33097         return result ? this.dropAllowed : this.dropNotAllowed;
33098     },
33099
33100     onNodeOut : function(n, dd, e, data){
33101         this.proxyTop.hide();
33102         this.proxyBottom.hide();
33103     },
33104
33105     onNodeDrop : function(n, dd, e, data){
33106         var h = data.header;
33107         if(h != n){
33108             var cm = this.grid.colModel;
33109             var x = Roo.lib.Event.getPageX(e);
33110             var r = Roo.lib.Dom.getRegion(n.firstChild);
33111             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33112             var oldIndex = this.view.getCellIndex(h);
33113             var newIndex = this.view.getCellIndex(n);
33114             var locked = cm.isLocked(newIndex);
33115             if(pt == "after"){
33116                 newIndex++;
33117             }
33118             if(oldIndex < newIndex){
33119                 newIndex--;
33120             }
33121             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33122                 return false;
33123             }
33124             cm.setLocked(oldIndex, locked, true);
33125             cm.moveColumn(oldIndex, newIndex);
33126             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33127             return true;
33128         }
33129         return false;
33130     }
33131 });
33132 /*
33133  * Based on:
33134  * Ext JS Library 1.1.1
33135  * Copyright(c) 2006-2007, Ext JS, LLC.
33136  *
33137  * Originally Released Under LGPL - original licence link has changed is not relivant.
33138  *
33139  * Fork - LGPL
33140  * <script type="text/javascript">
33141  */
33142   
33143 /**
33144  * @class Roo.grid.GridView
33145  * @extends Roo.util.Observable
33146  *
33147  * @constructor
33148  * @param {Object} config
33149  */
33150 Roo.grid.GridView = function(config){
33151     Roo.grid.GridView.superclass.constructor.call(this);
33152     this.el = null;
33153
33154     Roo.apply(this, config);
33155 };
33156
33157 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33158
33159     unselectable :  'unselectable="on"',
33160     unselectableCls :  'x-unselectable',
33161     
33162     
33163     rowClass : "x-grid-row",
33164
33165     cellClass : "x-grid-col",
33166
33167     tdClass : "x-grid-td",
33168
33169     hdClass : "x-grid-hd",
33170
33171     splitClass : "x-grid-split",
33172
33173     sortClasses : ["sort-asc", "sort-desc"],
33174
33175     enableMoveAnim : false,
33176
33177     hlColor: "C3DAF9",
33178
33179     dh : Roo.DomHelper,
33180
33181     fly : Roo.Element.fly,
33182
33183     css : Roo.util.CSS,
33184
33185     borderWidth: 1,
33186
33187     splitOffset: 3,
33188
33189     scrollIncrement : 22,
33190
33191     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33192
33193     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33194
33195     bind : function(ds, cm){
33196         if(this.ds){
33197             this.ds.un("load", this.onLoad, this);
33198             this.ds.un("datachanged", this.onDataChange, this);
33199             this.ds.un("add", this.onAdd, this);
33200             this.ds.un("remove", this.onRemove, this);
33201             this.ds.un("update", this.onUpdate, this);
33202             this.ds.un("clear", this.onClear, this);
33203         }
33204         if(ds){
33205             ds.on("load", this.onLoad, this);
33206             ds.on("datachanged", this.onDataChange, this);
33207             ds.on("add", this.onAdd, this);
33208             ds.on("remove", this.onRemove, this);
33209             ds.on("update", this.onUpdate, this);
33210             ds.on("clear", this.onClear, this);
33211         }
33212         this.ds = ds;
33213
33214         if(this.cm){
33215             this.cm.un("widthchange", this.onColWidthChange, this);
33216             this.cm.un("headerchange", this.onHeaderChange, this);
33217             this.cm.un("hiddenchange", this.onHiddenChange, this);
33218             this.cm.un("columnmoved", this.onColumnMove, this);
33219             this.cm.un("columnlockchange", this.onColumnLock, this);
33220         }
33221         if(cm){
33222             this.generateRules(cm);
33223             cm.on("widthchange", this.onColWidthChange, this);
33224             cm.on("headerchange", this.onHeaderChange, this);
33225             cm.on("hiddenchange", this.onHiddenChange, this);
33226             cm.on("columnmoved", this.onColumnMove, this);
33227             cm.on("columnlockchange", this.onColumnLock, this);
33228         }
33229         this.cm = cm;
33230     },
33231
33232     init: function(grid){
33233         Roo.grid.GridView.superclass.init.call(this, grid);
33234
33235         this.bind(grid.dataSource, grid.colModel);
33236
33237         grid.on("headerclick", this.handleHeaderClick, this);
33238
33239         if(grid.trackMouseOver){
33240             grid.on("mouseover", this.onRowOver, this);
33241             grid.on("mouseout", this.onRowOut, this);
33242         }
33243         grid.cancelTextSelection = function(){};
33244         this.gridId = grid.id;
33245
33246         var tpls = this.templates || {};
33247
33248         if(!tpls.master){
33249             tpls.master = new Roo.Template(
33250                '<div class="x-grid" hidefocus="true">',
33251                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33252                   '<div class="x-grid-topbar"></div>',
33253                   '<div class="x-grid-scroller"><div></div></div>',
33254                   '<div class="x-grid-locked">',
33255                       '<div class="x-grid-header">{lockedHeader}</div>',
33256                       '<div class="x-grid-body">{lockedBody}</div>',
33257                   "</div>",
33258                   '<div class="x-grid-viewport">',
33259                       '<div class="x-grid-header">{header}</div>',
33260                       '<div class="x-grid-body">{body}</div>',
33261                   "</div>",
33262                   '<div class="x-grid-bottombar"></div>',
33263                  
33264                   '<div class="x-grid-resize-proxy">&#160;</div>',
33265                "</div>"
33266             );
33267             tpls.master.disableformats = true;
33268         }
33269
33270         if(!tpls.header){
33271             tpls.header = new Roo.Template(
33272                '<table border="0" cellspacing="0" cellpadding="0">',
33273                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33274                "</table>{splits}"
33275             );
33276             tpls.header.disableformats = true;
33277         }
33278         tpls.header.compile();
33279
33280         if(!tpls.hcell){
33281             tpls.hcell = new Roo.Template(
33282                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33283                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33284                 "</div></td>"
33285              );
33286              tpls.hcell.disableFormats = true;
33287         }
33288         tpls.hcell.compile();
33289
33290         if(!tpls.hsplit){
33291             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33292                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33293             tpls.hsplit.disableFormats = true;
33294         }
33295         tpls.hsplit.compile();
33296
33297         if(!tpls.body){
33298             tpls.body = new Roo.Template(
33299                '<table border="0" cellspacing="0" cellpadding="0">',
33300                "<tbody>{rows}</tbody>",
33301                "</table>"
33302             );
33303             tpls.body.disableFormats = true;
33304         }
33305         tpls.body.compile();
33306
33307         if(!tpls.row){
33308             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33309             tpls.row.disableFormats = true;
33310         }
33311         tpls.row.compile();
33312
33313         if(!tpls.cell){
33314             tpls.cell = new Roo.Template(
33315                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33316                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33317                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33318                 "</td>"
33319             );
33320             tpls.cell.disableFormats = true;
33321         }
33322         tpls.cell.compile();
33323
33324         this.templates = tpls;
33325     },
33326
33327     // remap these for backwards compat
33328     onColWidthChange : function(){
33329         this.updateColumns.apply(this, arguments);
33330     },
33331     onHeaderChange : function(){
33332         this.updateHeaders.apply(this, arguments);
33333     }, 
33334     onHiddenChange : function(){
33335         this.handleHiddenChange.apply(this, arguments);
33336     },
33337     onColumnMove : function(){
33338         this.handleColumnMove.apply(this, arguments);
33339     },
33340     onColumnLock : function(){
33341         this.handleLockChange.apply(this, arguments);
33342     },
33343
33344     onDataChange : function(){
33345         this.refresh();
33346         this.updateHeaderSortState();
33347     },
33348
33349     onClear : function(){
33350         this.refresh();
33351     },
33352
33353     onUpdate : function(ds, record){
33354         this.refreshRow(record);
33355     },
33356
33357     refreshRow : function(record){
33358         var ds = this.ds, index;
33359         if(typeof record == 'number'){
33360             index = record;
33361             record = ds.getAt(index);
33362         }else{
33363             index = ds.indexOf(record);
33364         }
33365         this.insertRows(ds, index, index, true);
33366         this.onRemove(ds, record, index+1, true);
33367         this.syncRowHeights(index, index);
33368         this.layout();
33369         this.fireEvent("rowupdated", this, index, record);
33370     },
33371
33372     onAdd : function(ds, records, index){
33373         this.insertRows(ds, index, index + (records.length-1));
33374     },
33375
33376     onRemove : function(ds, record, index, isUpdate){
33377         if(isUpdate !== true){
33378             this.fireEvent("beforerowremoved", this, index, record);
33379         }
33380         var bt = this.getBodyTable(), lt = this.getLockedTable();
33381         if(bt.rows[index]){
33382             bt.firstChild.removeChild(bt.rows[index]);
33383         }
33384         if(lt.rows[index]){
33385             lt.firstChild.removeChild(lt.rows[index]);
33386         }
33387         if(isUpdate !== true){
33388             this.stripeRows(index);
33389             this.syncRowHeights(index, index);
33390             this.layout();
33391             this.fireEvent("rowremoved", this, index, record);
33392         }
33393     },
33394
33395     onLoad : function(){
33396         this.scrollToTop();
33397     },
33398
33399     /**
33400      * Scrolls the grid to the top
33401      */
33402     scrollToTop : function(){
33403         if(this.scroller){
33404             this.scroller.dom.scrollTop = 0;
33405             this.syncScroll();
33406         }
33407     },
33408
33409     /**
33410      * Gets a panel in the header of the grid that can be used for toolbars etc.
33411      * After modifying the contents of this panel a call to grid.autoSize() may be
33412      * required to register any changes in size.
33413      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33414      * @return Roo.Element
33415      */
33416     getHeaderPanel : function(doShow){
33417         if(doShow){
33418             this.headerPanel.show();
33419         }
33420         return this.headerPanel;
33421     },
33422
33423     /**
33424      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33425      * After modifying the contents of this panel a call to grid.autoSize() may be
33426      * required to register any changes in size.
33427      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33428      * @return Roo.Element
33429      */
33430     getFooterPanel : function(doShow){
33431         if(doShow){
33432             this.footerPanel.show();
33433         }
33434         return this.footerPanel;
33435     },
33436
33437     initElements : function(){
33438         var E = Roo.Element;
33439         var el = this.grid.getGridEl().dom.firstChild;
33440         var cs = el.childNodes;
33441
33442         this.el = new E(el);
33443         
33444          this.focusEl = new E(el.firstChild);
33445         this.focusEl.swallowEvent("click", true);
33446         
33447         this.headerPanel = new E(cs[1]);
33448         this.headerPanel.enableDisplayMode("block");
33449
33450         this.scroller = new E(cs[2]);
33451         this.scrollSizer = new E(this.scroller.dom.firstChild);
33452
33453         this.lockedWrap = new E(cs[3]);
33454         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33455         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33456
33457         this.mainWrap = new E(cs[4]);
33458         this.mainHd = new E(this.mainWrap.dom.firstChild);
33459         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33460
33461         this.footerPanel = new E(cs[5]);
33462         this.footerPanel.enableDisplayMode("block");
33463
33464         this.resizeProxy = new E(cs[6]);
33465
33466         this.headerSelector = String.format(
33467            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33468            this.lockedHd.id, this.mainHd.id
33469         );
33470
33471         this.splitterSelector = String.format(
33472            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33473            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33474         );
33475     },
33476     idToCssName : function(s)
33477     {
33478         return s.replace(/[^a-z0-9]+/ig, '-');
33479     },
33480
33481     getHeaderCell : function(index){
33482         return Roo.DomQuery.select(this.headerSelector)[index];
33483     },
33484
33485     getHeaderCellMeasure : function(index){
33486         return this.getHeaderCell(index).firstChild;
33487     },
33488
33489     getHeaderCellText : function(index){
33490         return this.getHeaderCell(index).firstChild.firstChild;
33491     },
33492
33493     getLockedTable : function(){
33494         return this.lockedBody.dom.firstChild;
33495     },
33496
33497     getBodyTable : function(){
33498         return this.mainBody.dom.firstChild;
33499     },
33500
33501     getLockedRow : function(index){
33502         return this.getLockedTable().rows[index];
33503     },
33504
33505     getRow : function(index){
33506         return this.getBodyTable().rows[index];
33507     },
33508
33509     getRowComposite : function(index){
33510         if(!this.rowEl){
33511             this.rowEl = new Roo.CompositeElementLite();
33512         }
33513         var els = [], lrow, mrow;
33514         if(lrow = this.getLockedRow(index)){
33515             els.push(lrow);
33516         }
33517         if(mrow = this.getRow(index)){
33518             els.push(mrow);
33519         }
33520         this.rowEl.elements = els;
33521         return this.rowEl;
33522     },
33523     /**
33524      * Gets the 'td' of the cell
33525      * 
33526      * @param {Integer} rowIndex row to select
33527      * @param {Integer} colIndex column to select
33528      * 
33529      * @return {Object} 
33530      */
33531     getCell : function(rowIndex, colIndex){
33532         var locked = this.cm.getLockedCount();
33533         var source;
33534         if(colIndex < locked){
33535             source = this.lockedBody.dom.firstChild;
33536         }else{
33537             source = this.mainBody.dom.firstChild;
33538             colIndex -= locked;
33539         }
33540         return source.rows[rowIndex].childNodes[colIndex];
33541     },
33542
33543     getCellText : function(rowIndex, colIndex){
33544         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33545     },
33546
33547     getCellBox : function(cell){
33548         var b = this.fly(cell).getBox();
33549         if(Roo.isOpera){ // opera fails to report the Y
33550             b.y = cell.offsetTop + this.mainBody.getY();
33551         }
33552         return b;
33553     },
33554
33555     getCellIndex : function(cell){
33556         var id = String(cell.className).match(this.cellRE);
33557         if(id){
33558             return parseInt(id[1], 10);
33559         }
33560         return 0;
33561     },
33562
33563     findHeaderIndex : function(n){
33564         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33565         return r ? this.getCellIndex(r) : false;
33566     },
33567
33568     findHeaderCell : function(n){
33569         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33570         return r ? r : false;
33571     },
33572
33573     findRowIndex : function(n){
33574         if(!n){
33575             return false;
33576         }
33577         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33578         return r ? r.rowIndex : false;
33579     },
33580
33581     findCellIndex : function(node){
33582         var stop = this.el.dom;
33583         while(node && node != stop){
33584             if(this.findRE.test(node.className)){
33585                 return this.getCellIndex(node);
33586             }
33587             node = node.parentNode;
33588         }
33589         return false;
33590     },
33591
33592     getColumnId : function(index){
33593         return this.cm.getColumnId(index);
33594     },
33595
33596     getSplitters : function()
33597     {
33598         if(this.splitterSelector){
33599            return Roo.DomQuery.select(this.splitterSelector);
33600         }else{
33601             return null;
33602       }
33603     },
33604
33605     getSplitter : function(index){
33606         return this.getSplitters()[index];
33607     },
33608
33609     onRowOver : function(e, t){
33610         var row;
33611         if((row = this.findRowIndex(t)) !== false){
33612             this.getRowComposite(row).addClass("x-grid-row-over");
33613         }
33614     },
33615
33616     onRowOut : function(e, t){
33617         var row;
33618         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33619             this.getRowComposite(row).removeClass("x-grid-row-over");
33620         }
33621     },
33622
33623     renderHeaders : function(){
33624         var cm = this.cm;
33625         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33626         var cb = [], lb = [], sb = [], lsb = [], p = {};
33627         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33628             p.cellId = "x-grid-hd-0-" + i;
33629             p.splitId = "x-grid-csplit-0-" + i;
33630             p.id = cm.getColumnId(i);
33631             p.value = cm.getColumnHeader(i) || "";
33632             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33633             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33634             if(!cm.isLocked(i)){
33635                 cb[cb.length] = ct.apply(p);
33636                 sb[sb.length] = st.apply(p);
33637             }else{
33638                 lb[lb.length] = ct.apply(p);
33639                 lsb[lsb.length] = st.apply(p);
33640             }
33641         }
33642         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33643                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33644     },
33645
33646     updateHeaders : function(){
33647         var html = this.renderHeaders();
33648         this.lockedHd.update(html[0]);
33649         this.mainHd.update(html[1]);
33650     },
33651
33652     /**
33653      * Focuses the specified row.
33654      * @param {Number} row The row index
33655      */
33656     focusRow : function(row)
33657     {
33658         //Roo.log('GridView.focusRow');
33659         var x = this.scroller.dom.scrollLeft;
33660         this.focusCell(row, 0, false);
33661         this.scroller.dom.scrollLeft = x;
33662     },
33663
33664     /**
33665      * Focuses the specified cell.
33666      * @param {Number} row The row index
33667      * @param {Number} col The column index
33668      * @param {Boolean} hscroll false to disable horizontal scrolling
33669      */
33670     focusCell : function(row, col, hscroll)
33671     {
33672         //Roo.log('GridView.focusCell');
33673         var el = this.ensureVisible(row, col, hscroll);
33674         this.focusEl.alignTo(el, "tl-tl");
33675         if(Roo.isGecko){
33676             this.focusEl.focus();
33677         }else{
33678             this.focusEl.focus.defer(1, this.focusEl);
33679         }
33680     },
33681
33682     /**
33683      * Scrolls the specified cell into view
33684      * @param {Number} row The row index
33685      * @param {Number} col The column index
33686      * @param {Boolean} hscroll false to disable horizontal scrolling
33687      */
33688     ensureVisible : function(row, col, hscroll)
33689     {
33690         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33691         //return null; //disable for testing.
33692         if(typeof row != "number"){
33693             row = row.rowIndex;
33694         }
33695         if(row < 0 && row >= this.ds.getCount()){
33696             return  null;
33697         }
33698         col = (col !== undefined ? col : 0);
33699         var cm = this.grid.colModel;
33700         while(cm.isHidden(col)){
33701             col++;
33702         }
33703
33704         var el = this.getCell(row, col);
33705         if(!el){
33706             return null;
33707         }
33708         var c = this.scroller.dom;
33709
33710         var ctop = parseInt(el.offsetTop, 10);
33711         var cleft = parseInt(el.offsetLeft, 10);
33712         var cbot = ctop + el.offsetHeight;
33713         var cright = cleft + el.offsetWidth;
33714         
33715         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33716         var stop = parseInt(c.scrollTop, 10);
33717         var sleft = parseInt(c.scrollLeft, 10);
33718         var sbot = stop + ch;
33719         var sright = sleft + c.clientWidth;
33720         /*
33721         Roo.log('GridView.ensureVisible:' +
33722                 ' ctop:' + ctop +
33723                 ' c.clientHeight:' + c.clientHeight +
33724                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33725                 ' stop:' + stop +
33726                 ' cbot:' + cbot +
33727                 ' sbot:' + sbot +
33728                 ' ch:' + ch  
33729                 );
33730         */
33731         if(ctop < stop){
33732              c.scrollTop = ctop;
33733             //Roo.log("set scrolltop to ctop DISABLE?");
33734         }else if(cbot > sbot){
33735             //Roo.log("set scrolltop to cbot-ch");
33736             c.scrollTop = cbot-ch;
33737         }
33738         
33739         if(hscroll !== false){
33740             if(cleft < sleft){
33741                 c.scrollLeft = cleft;
33742             }else if(cright > sright){
33743                 c.scrollLeft = cright-c.clientWidth;
33744             }
33745         }
33746          
33747         return el;
33748     },
33749
33750     updateColumns : function(){
33751         this.grid.stopEditing();
33752         var cm = this.grid.colModel, colIds = this.getColumnIds();
33753         //var totalWidth = cm.getTotalWidth();
33754         var pos = 0;
33755         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33756             //if(cm.isHidden(i)) continue;
33757             var w = cm.getColumnWidth(i);
33758             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33759             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33760         }
33761         this.updateSplitters();
33762     },
33763
33764     generateRules : function(cm){
33765         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33766         Roo.util.CSS.removeStyleSheet(rulesId);
33767         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33768             var cid = cm.getColumnId(i);
33769             var align = '';
33770             if(cm.config[i].align){
33771                 align = 'text-align:'+cm.config[i].align+';';
33772             }
33773             var hidden = '';
33774             if(cm.isHidden(i)){
33775                 hidden = 'display:none;';
33776             }
33777             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33778             ruleBuf.push(
33779                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33780                     this.hdSelector, cid, " {\n", align, width, "}\n",
33781                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33782                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33783         }
33784         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33785     },
33786
33787     updateSplitters : function(){
33788         var cm = this.cm, s = this.getSplitters();
33789         if(s){ // splitters not created yet
33790             var pos = 0, locked = true;
33791             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33792                 if(cm.isHidden(i)) {
33793                     continue;
33794                 }
33795                 var w = cm.getColumnWidth(i); // make sure it's a number
33796                 if(!cm.isLocked(i) && locked){
33797                     pos = 0;
33798                     locked = false;
33799                 }
33800                 pos += w;
33801                 s[i].style.left = (pos-this.splitOffset) + "px";
33802             }
33803         }
33804     },
33805
33806     handleHiddenChange : function(colModel, colIndex, hidden){
33807         if(hidden){
33808             this.hideColumn(colIndex);
33809         }else{
33810             this.unhideColumn(colIndex);
33811         }
33812     },
33813
33814     hideColumn : function(colIndex){
33815         var cid = this.getColumnId(colIndex);
33816         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33817         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33818         if(Roo.isSafari){
33819             this.updateHeaders();
33820         }
33821         this.updateSplitters();
33822         this.layout();
33823     },
33824
33825     unhideColumn : function(colIndex){
33826         var cid = this.getColumnId(colIndex);
33827         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33828         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33829
33830         if(Roo.isSafari){
33831             this.updateHeaders();
33832         }
33833         this.updateSplitters();
33834         this.layout();
33835     },
33836
33837     insertRows : function(dm, firstRow, lastRow, isUpdate){
33838         if(firstRow == 0 && lastRow == dm.getCount()-1){
33839             this.refresh();
33840         }else{
33841             if(!isUpdate){
33842                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33843             }
33844             var s = this.getScrollState();
33845             var markup = this.renderRows(firstRow, lastRow);
33846             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33847             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33848             this.restoreScroll(s);
33849             if(!isUpdate){
33850                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33851                 this.syncRowHeights(firstRow, lastRow);
33852                 this.stripeRows(firstRow);
33853                 this.layout();
33854             }
33855         }
33856     },
33857
33858     bufferRows : function(markup, target, index){
33859         var before = null, trows = target.rows, tbody = target.tBodies[0];
33860         if(index < trows.length){
33861             before = trows[index];
33862         }
33863         var b = document.createElement("div");
33864         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33865         var rows = b.firstChild.rows;
33866         for(var i = 0, len = rows.length; i < len; i++){
33867             if(before){
33868                 tbody.insertBefore(rows[0], before);
33869             }else{
33870                 tbody.appendChild(rows[0]);
33871             }
33872         }
33873         b.innerHTML = "";
33874         b = null;
33875     },
33876
33877     deleteRows : function(dm, firstRow, lastRow){
33878         if(dm.getRowCount()<1){
33879             this.fireEvent("beforerefresh", this);
33880             this.mainBody.update("");
33881             this.lockedBody.update("");
33882             this.fireEvent("refresh", this);
33883         }else{
33884             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33885             var bt = this.getBodyTable();
33886             var tbody = bt.firstChild;
33887             var rows = bt.rows;
33888             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33889                 tbody.removeChild(rows[firstRow]);
33890             }
33891             this.stripeRows(firstRow);
33892             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33893         }
33894     },
33895
33896     updateRows : function(dataSource, firstRow, lastRow){
33897         var s = this.getScrollState();
33898         this.refresh();
33899         this.restoreScroll(s);
33900     },
33901
33902     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33903         if(!noRefresh){
33904            this.refresh();
33905         }
33906         this.updateHeaderSortState();
33907     },
33908
33909     getScrollState : function(){
33910         
33911         var sb = this.scroller.dom;
33912         return {left: sb.scrollLeft, top: sb.scrollTop};
33913     },
33914
33915     stripeRows : function(startRow){
33916         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33917             return;
33918         }
33919         startRow = startRow || 0;
33920         var rows = this.getBodyTable().rows;
33921         var lrows = this.getLockedTable().rows;
33922         var cls = ' x-grid-row-alt ';
33923         for(var i = startRow, len = rows.length; i < len; i++){
33924             var row = rows[i], lrow = lrows[i];
33925             var isAlt = ((i+1) % 2 == 0);
33926             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33927             if(isAlt == hasAlt){
33928                 continue;
33929             }
33930             if(isAlt){
33931                 row.className += " x-grid-row-alt";
33932             }else{
33933                 row.className = row.className.replace("x-grid-row-alt", "");
33934             }
33935             if(lrow){
33936                 lrow.className = row.className;
33937             }
33938         }
33939     },
33940
33941     restoreScroll : function(state){
33942         //Roo.log('GridView.restoreScroll');
33943         var sb = this.scroller.dom;
33944         sb.scrollLeft = state.left;
33945         sb.scrollTop = state.top;
33946         this.syncScroll();
33947     },
33948
33949     syncScroll : function(){
33950         //Roo.log('GridView.syncScroll');
33951         var sb = this.scroller.dom;
33952         var sh = this.mainHd.dom;
33953         var bs = this.mainBody.dom;
33954         var lv = this.lockedBody.dom;
33955         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33956         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33957     },
33958
33959     handleScroll : function(e){
33960         this.syncScroll();
33961         var sb = this.scroller.dom;
33962         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33963         e.stopEvent();
33964     },
33965
33966     handleWheel : function(e){
33967         var d = e.getWheelDelta();
33968         this.scroller.dom.scrollTop -= d*22;
33969         // set this here to prevent jumpy scrolling on large tables
33970         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33971         e.stopEvent();
33972     },
33973
33974     renderRows : function(startRow, endRow){
33975         // pull in all the crap needed to render rows
33976         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33977         var colCount = cm.getColumnCount();
33978
33979         if(ds.getCount() < 1){
33980             return ["", ""];
33981         }
33982
33983         // build a map for all the columns
33984         var cs = [];
33985         for(var i = 0; i < colCount; i++){
33986             var name = cm.getDataIndex(i);
33987             cs[i] = {
33988                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33989                 renderer : cm.getRenderer(i),
33990                 id : cm.getColumnId(i),
33991                 locked : cm.isLocked(i),
33992                 has_editor : cm.isCellEditable(i)
33993             };
33994         }
33995
33996         startRow = startRow || 0;
33997         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33998
33999         // records to render
34000         var rs = ds.getRange(startRow, endRow);
34001
34002         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34003     },
34004
34005     // As much as I hate to duplicate code, this was branched because FireFox really hates
34006     // [].join("") on strings. The performance difference was substantial enough to
34007     // branch this function
34008     doRender : Roo.isGecko ?
34009             function(cs, rs, ds, startRow, colCount, stripe){
34010                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34011                 // buffers
34012                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34013                 
34014                 var hasListener = this.grid.hasListener('rowclass');
34015                 var rowcfg = {};
34016                 for(var j = 0, len = rs.length; j < len; j++){
34017                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34018                     for(var i = 0; i < colCount; i++){
34019                         c = cs[i];
34020                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34021                         p.id = c.id;
34022                         p.css = p.attr = "";
34023                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34024                         if(p.value == undefined || p.value === "") {
34025                             p.value = "&#160;";
34026                         }
34027                         if(c.has_editor){
34028                             p.css += ' x-grid-editable-cell';
34029                         }
34030                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34031                             p.css +=  ' x-grid-dirty-cell';
34032                         }
34033                         var markup = ct.apply(p);
34034                         if(!c.locked){
34035                             cb+= markup;
34036                         }else{
34037                             lcb+= markup;
34038                         }
34039                     }
34040                     var alt = [];
34041                     if(stripe && ((rowIndex+1) % 2 == 0)){
34042                         alt.push("x-grid-row-alt")
34043                     }
34044                     if(r.dirty){
34045                         alt.push(  " x-grid-dirty-row");
34046                     }
34047                     rp.cells = lcb;
34048                     if(this.getRowClass){
34049                         alt.push(this.getRowClass(r, rowIndex));
34050                     }
34051                     if (hasListener) {
34052                         rowcfg = {
34053                              
34054                             record: r,
34055                             rowIndex : rowIndex,
34056                             rowClass : ''
34057                         };
34058                         this.grid.fireEvent('rowclass', this, rowcfg);
34059                         alt.push(rowcfg.rowClass);
34060                     }
34061                     rp.alt = alt.join(" ");
34062                     lbuf+= rt.apply(rp);
34063                     rp.cells = cb;
34064                     buf+=  rt.apply(rp);
34065                 }
34066                 return [lbuf, buf];
34067             } :
34068             function(cs, rs, ds, startRow, colCount, stripe){
34069                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34070                 // buffers
34071                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34072                 var hasListener = this.grid.hasListener('rowclass');
34073  
34074                 var rowcfg = {};
34075                 for(var j = 0, len = rs.length; j < len; j++){
34076                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34077                     for(var i = 0; i < colCount; i++){
34078                         c = cs[i];
34079                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34080                         p.id = c.id;
34081                         p.css = p.attr = "";
34082                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34083                         if(p.value == undefined || p.value === "") {
34084                             p.value = "&#160;";
34085                         }
34086                         //Roo.log(c);
34087                          if(c.has_editor){
34088                             p.css += ' x-grid-editable-cell';
34089                         }
34090                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34091                             p.css += ' x-grid-dirty-cell' 
34092                         }
34093                         
34094                         var markup = ct.apply(p);
34095                         if(!c.locked){
34096                             cb[cb.length] = markup;
34097                         }else{
34098                             lcb[lcb.length] = markup;
34099                         }
34100                     }
34101                     var alt = [];
34102                     if(stripe && ((rowIndex+1) % 2 == 0)){
34103                         alt.push( "x-grid-row-alt");
34104                     }
34105                     if(r.dirty){
34106                         alt.push(" x-grid-dirty-row");
34107                     }
34108                     rp.cells = lcb;
34109                     if(this.getRowClass){
34110                         alt.push( this.getRowClass(r, rowIndex));
34111                     }
34112                     if (hasListener) {
34113                         rowcfg = {
34114                              
34115                             record: r,
34116                             rowIndex : rowIndex,
34117                             rowClass : ''
34118                         };
34119                         this.grid.fireEvent('rowclass', this, rowcfg);
34120                         alt.push(rowcfg.rowClass);
34121                     }
34122                     
34123                     rp.alt = alt.join(" ");
34124                     rp.cells = lcb.join("");
34125                     lbuf[lbuf.length] = rt.apply(rp);
34126                     rp.cells = cb.join("");
34127                     buf[buf.length] =  rt.apply(rp);
34128                 }
34129                 return [lbuf.join(""), buf.join("")];
34130             },
34131
34132     renderBody : function(){
34133         var markup = this.renderRows();
34134         var bt = this.templates.body;
34135         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34136     },
34137
34138     /**
34139      * Refreshes the grid
34140      * @param {Boolean} headersToo
34141      */
34142     refresh : function(headersToo){
34143         this.fireEvent("beforerefresh", this);
34144         this.grid.stopEditing();
34145         var result = this.renderBody();
34146         this.lockedBody.update(result[0]);
34147         this.mainBody.update(result[1]);
34148         if(headersToo === true){
34149             this.updateHeaders();
34150             this.updateColumns();
34151             this.updateSplitters();
34152             this.updateHeaderSortState();
34153         }
34154         this.syncRowHeights();
34155         this.layout();
34156         this.fireEvent("refresh", this);
34157     },
34158
34159     handleColumnMove : function(cm, oldIndex, newIndex){
34160         this.indexMap = null;
34161         var s = this.getScrollState();
34162         this.refresh(true);
34163         this.restoreScroll(s);
34164         this.afterMove(newIndex);
34165     },
34166
34167     afterMove : function(colIndex){
34168         if(this.enableMoveAnim && Roo.enableFx){
34169             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34170         }
34171         // if multisort - fix sortOrder, and reload..
34172         if (this.grid.dataSource.multiSort) {
34173             // the we can call sort again..
34174             var dm = this.grid.dataSource;
34175             var cm = this.grid.colModel;
34176             var so = [];
34177             for(var i = 0; i < cm.config.length; i++ ) {
34178                 
34179                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34180                     continue; // dont' bother, it's not in sort list or being set.
34181                 }
34182                 
34183                 so.push(cm.config[i].dataIndex);
34184             };
34185             dm.sortOrder = so;
34186             dm.load(dm.lastOptions);
34187             
34188             
34189         }
34190         
34191     },
34192
34193     updateCell : function(dm, rowIndex, dataIndex){
34194         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34195         if(typeof colIndex == "undefined"){ // not present in grid
34196             return;
34197         }
34198         var cm = this.grid.colModel;
34199         var cell = this.getCell(rowIndex, colIndex);
34200         var cellText = this.getCellText(rowIndex, colIndex);
34201
34202         var p = {
34203             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34204             id : cm.getColumnId(colIndex),
34205             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34206         };
34207         var renderer = cm.getRenderer(colIndex);
34208         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34209         if(typeof val == "undefined" || val === "") {
34210             val = "&#160;";
34211         }
34212         cellText.innerHTML = val;
34213         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34214         this.syncRowHeights(rowIndex, rowIndex);
34215     },
34216
34217     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34218         var maxWidth = 0;
34219         if(this.grid.autoSizeHeaders){
34220             var h = this.getHeaderCellMeasure(colIndex);
34221             maxWidth = Math.max(maxWidth, h.scrollWidth);
34222         }
34223         var tb, index;
34224         if(this.cm.isLocked(colIndex)){
34225             tb = this.getLockedTable();
34226             index = colIndex;
34227         }else{
34228             tb = this.getBodyTable();
34229             index = colIndex - this.cm.getLockedCount();
34230         }
34231         if(tb && tb.rows){
34232             var rows = tb.rows;
34233             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34234             for(var i = 0; i < stopIndex; i++){
34235                 var cell = rows[i].childNodes[index].firstChild;
34236                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34237             }
34238         }
34239         return maxWidth + /*margin for error in IE*/ 5;
34240     },
34241     /**
34242      * Autofit a column to its content.
34243      * @param {Number} colIndex
34244      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34245      */
34246      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34247          if(this.cm.isHidden(colIndex)){
34248              return; // can't calc a hidden column
34249          }
34250         if(forceMinSize){
34251             var cid = this.cm.getColumnId(colIndex);
34252             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34253            if(this.grid.autoSizeHeaders){
34254                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34255            }
34256         }
34257         var newWidth = this.calcColumnWidth(colIndex);
34258         this.cm.setColumnWidth(colIndex,
34259             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34260         if(!suppressEvent){
34261             this.grid.fireEvent("columnresize", colIndex, newWidth);
34262         }
34263     },
34264
34265     /**
34266      * Autofits all columns to their content and then expands to fit any extra space in the grid
34267      */
34268      autoSizeColumns : function(){
34269         var cm = this.grid.colModel;
34270         var colCount = cm.getColumnCount();
34271         for(var i = 0; i < colCount; i++){
34272             this.autoSizeColumn(i, true, true);
34273         }
34274         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34275             this.fitColumns();
34276         }else{
34277             this.updateColumns();
34278             this.layout();
34279         }
34280     },
34281
34282     /**
34283      * Autofits all columns to the grid's width proportionate with their current size
34284      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34285      */
34286     fitColumns : function(reserveScrollSpace){
34287         var cm = this.grid.colModel;
34288         var colCount = cm.getColumnCount();
34289         var cols = [];
34290         var width = 0;
34291         var i, w;
34292         for (i = 0; i < colCount; i++){
34293             if(!cm.isHidden(i) && !cm.isFixed(i)){
34294                 w = cm.getColumnWidth(i);
34295                 cols.push(i);
34296                 cols.push(w);
34297                 width += w;
34298             }
34299         }
34300         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34301         if(reserveScrollSpace){
34302             avail -= 17;
34303         }
34304         var frac = (avail - cm.getTotalWidth())/width;
34305         while (cols.length){
34306             w = cols.pop();
34307             i = cols.pop();
34308             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34309         }
34310         this.updateColumns();
34311         this.layout();
34312     },
34313
34314     onRowSelect : function(rowIndex){
34315         var row = this.getRowComposite(rowIndex);
34316         row.addClass("x-grid-row-selected");
34317     },
34318
34319     onRowDeselect : function(rowIndex){
34320         var row = this.getRowComposite(rowIndex);
34321         row.removeClass("x-grid-row-selected");
34322     },
34323
34324     onCellSelect : function(row, col){
34325         var cell = this.getCell(row, col);
34326         if(cell){
34327             Roo.fly(cell).addClass("x-grid-cell-selected");
34328         }
34329     },
34330
34331     onCellDeselect : function(row, col){
34332         var cell = this.getCell(row, col);
34333         if(cell){
34334             Roo.fly(cell).removeClass("x-grid-cell-selected");
34335         }
34336     },
34337
34338     updateHeaderSortState : function(){
34339         
34340         // sort state can be single { field: xxx, direction : yyy}
34341         // or   { xxx=>ASC , yyy : DESC ..... }
34342         
34343         var mstate = {};
34344         if (!this.ds.multiSort) { 
34345             var state = this.ds.getSortState();
34346             if(!state){
34347                 return;
34348             }
34349             mstate[state.field] = state.direction;
34350             // FIXME... - this is not used here.. but might be elsewhere..
34351             this.sortState = state;
34352             
34353         } else {
34354             mstate = this.ds.sortToggle;
34355         }
34356         //remove existing sort classes..
34357         
34358         var sc = this.sortClasses;
34359         var hds = this.el.select(this.headerSelector).removeClass(sc);
34360         
34361         for(var f in mstate) {
34362         
34363             var sortColumn = this.cm.findColumnIndex(f);
34364             
34365             if(sortColumn != -1){
34366                 var sortDir = mstate[f];        
34367                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34368             }
34369         }
34370         
34371          
34372         
34373     },
34374
34375
34376     handleHeaderClick : function(g, index,e){
34377         
34378         Roo.log("header click");
34379         
34380         if (Roo.isTouch) {
34381             // touch events on header are handled by context
34382             this.handleHdCtx(g,index,e);
34383             return;
34384         }
34385         
34386         
34387         if(this.headersDisabled){
34388             return;
34389         }
34390         var dm = g.dataSource, cm = g.colModel;
34391         if(!cm.isSortable(index)){
34392             return;
34393         }
34394         g.stopEditing();
34395         
34396         if (dm.multiSort) {
34397             // update the sortOrder
34398             var so = [];
34399             for(var i = 0; i < cm.config.length; i++ ) {
34400                 
34401                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34402                     continue; // dont' bother, it's not in sort list or being set.
34403                 }
34404                 
34405                 so.push(cm.config[i].dataIndex);
34406             };
34407             dm.sortOrder = so;
34408         }
34409         
34410         
34411         dm.sort(cm.getDataIndex(index));
34412     },
34413
34414
34415     destroy : function(){
34416         if(this.colMenu){
34417             this.colMenu.removeAll();
34418             Roo.menu.MenuMgr.unregister(this.colMenu);
34419             this.colMenu.getEl().remove();
34420             delete this.colMenu;
34421         }
34422         if(this.hmenu){
34423             this.hmenu.removeAll();
34424             Roo.menu.MenuMgr.unregister(this.hmenu);
34425             this.hmenu.getEl().remove();
34426             delete this.hmenu;
34427         }
34428         if(this.grid.enableColumnMove){
34429             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34430             if(dds){
34431                 for(var dd in dds){
34432                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34433                         var elid = dds[dd].dragElId;
34434                         dds[dd].unreg();
34435                         Roo.get(elid).remove();
34436                     } else if(dds[dd].config.isTarget){
34437                         dds[dd].proxyTop.remove();
34438                         dds[dd].proxyBottom.remove();
34439                         dds[dd].unreg();
34440                     }
34441                     if(Roo.dd.DDM.locationCache[dd]){
34442                         delete Roo.dd.DDM.locationCache[dd];
34443                     }
34444                 }
34445                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34446             }
34447         }
34448         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34449         this.bind(null, null);
34450         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34451     },
34452
34453     handleLockChange : function(){
34454         this.refresh(true);
34455     },
34456
34457     onDenyColumnLock : function(){
34458
34459     },
34460
34461     onDenyColumnHide : function(){
34462
34463     },
34464
34465     handleHdMenuClick : function(item){
34466         var index = this.hdCtxIndex;
34467         var cm = this.cm, ds = this.ds;
34468         switch(item.id){
34469             case "asc":
34470                 ds.sort(cm.getDataIndex(index), "ASC");
34471                 break;
34472             case "desc":
34473                 ds.sort(cm.getDataIndex(index), "DESC");
34474                 break;
34475             case "lock":
34476                 var lc = cm.getLockedCount();
34477                 if(cm.getColumnCount(true) <= lc+1){
34478                     this.onDenyColumnLock();
34479                     return;
34480                 }
34481                 if(lc != index){
34482                     cm.setLocked(index, true, true);
34483                     cm.moveColumn(index, lc);
34484                     this.grid.fireEvent("columnmove", index, lc);
34485                 }else{
34486                     cm.setLocked(index, true);
34487                 }
34488             break;
34489             case "unlock":
34490                 var lc = cm.getLockedCount();
34491                 if((lc-1) != index){
34492                     cm.setLocked(index, false, true);
34493                     cm.moveColumn(index, lc-1);
34494                     this.grid.fireEvent("columnmove", index, lc-1);
34495                 }else{
34496                     cm.setLocked(index, false);
34497                 }
34498             break;
34499             case 'wider': // used to expand cols on touch..
34500             case 'narrow':
34501                 var cw = cm.getColumnWidth(index);
34502                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34503                 cw = Math.max(0, cw);
34504                 cw = Math.min(cw,4000);
34505                 cm.setColumnWidth(index, cw);
34506                 break;
34507                 
34508             default:
34509                 index = cm.getIndexById(item.id.substr(4));
34510                 if(index != -1){
34511                     if(item.checked && cm.getColumnCount(true) <= 1){
34512                         this.onDenyColumnHide();
34513                         return false;
34514                     }
34515                     cm.setHidden(index, item.checked);
34516                 }
34517         }
34518         return true;
34519     },
34520
34521     beforeColMenuShow : function(){
34522         var cm = this.cm,  colCount = cm.getColumnCount();
34523         this.colMenu.removeAll();
34524         for(var i = 0; i < colCount; i++){
34525             this.colMenu.add(new Roo.menu.CheckItem({
34526                 id: "col-"+cm.getColumnId(i),
34527                 text: cm.getColumnHeader(i),
34528                 checked: !cm.isHidden(i),
34529                 hideOnClick:false
34530             }));
34531         }
34532     },
34533
34534     handleHdCtx : function(g, index, e){
34535         e.stopEvent();
34536         var hd = this.getHeaderCell(index);
34537         this.hdCtxIndex = index;
34538         var ms = this.hmenu.items, cm = this.cm;
34539         ms.get("asc").setDisabled(!cm.isSortable(index));
34540         ms.get("desc").setDisabled(!cm.isSortable(index));
34541         if(this.grid.enableColLock !== false){
34542             ms.get("lock").setDisabled(cm.isLocked(index));
34543             ms.get("unlock").setDisabled(!cm.isLocked(index));
34544         }
34545         this.hmenu.show(hd, "tl-bl");
34546     },
34547
34548     handleHdOver : function(e){
34549         var hd = this.findHeaderCell(e.getTarget());
34550         if(hd && !this.headersDisabled){
34551             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34552                this.fly(hd).addClass("x-grid-hd-over");
34553             }
34554         }
34555     },
34556
34557     handleHdOut : function(e){
34558         var hd = this.findHeaderCell(e.getTarget());
34559         if(hd){
34560             this.fly(hd).removeClass("x-grid-hd-over");
34561         }
34562     },
34563
34564     handleSplitDblClick : function(e, t){
34565         var i = this.getCellIndex(t);
34566         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34567             this.autoSizeColumn(i, true);
34568             this.layout();
34569         }
34570     },
34571
34572     render : function(){
34573
34574         var cm = this.cm;
34575         var colCount = cm.getColumnCount();
34576
34577         if(this.grid.monitorWindowResize === true){
34578             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34579         }
34580         var header = this.renderHeaders();
34581         var body = this.templates.body.apply({rows:""});
34582         var html = this.templates.master.apply({
34583             lockedBody: body,
34584             body: body,
34585             lockedHeader: header[0],
34586             header: header[1]
34587         });
34588
34589         //this.updateColumns();
34590
34591         this.grid.getGridEl().dom.innerHTML = html;
34592
34593         this.initElements();
34594         
34595         // a kludge to fix the random scolling effect in webkit
34596         this.el.on("scroll", function() {
34597             this.el.dom.scrollTop=0; // hopefully not recursive..
34598         },this);
34599
34600         this.scroller.on("scroll", this.handleScroll, this);
34601         this.lockedBody.on("mousewheel", this.handleWheel, this);
34602         this.mainBody.on("mousewheel", this.handleWheel, this);
34603
34604         this.mainHd.on("mouseover", this.handleHdOver, this);
34605         this.mainHd.on("mouseout", this.handleHdOut, this);
34606         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34607                 {delegate: "."+this.splitClass});
34608
34609         this.lockedHd.on("mouseover", this.handleHdOver, this);
34610         this.lockedHd.on("mouseout", this.handleHdOut, this);
34611         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34612                 {delegate: "."+this.splitClass});
34613
34614         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34615             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34616         }
34617
34618         this.updateSplitters();
34619
34620         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34621             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34622             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34623         }
34624
34625         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34626             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34627             this.hmenu.add(
34628                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34629                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34630             );
34631             if(this.grid.enableColLock !== false){
34632                 this.hmenu.add('-',
34633                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34634                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34635                 );
34636             }
34637             if (Roo.isTouch) {
34638                  this.hmenu.add('-',
34639                     {id:"wider", text: this.columnsWiderText},
34640                     {id:"narrow", text: this.columnsNarrowText }
34641                 );
34642                 
34643                  
34644             }
34645             
34646             if(this.grid.enableColumnHide !== false){
34647
34648                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34649                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34650                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34651
34652                 this.hmenu.add('-',
34653                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34654                 );
34655             }
34656             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34657
34658             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34659         }
34660
34661         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34662             this.dd = new Roo.grid.GridDragZone(this.grid, {
34663                 ddGroup : this.grid.ddGroup || 'GridDD'
34664             });
34665             
34666         }
34667
34668         /*
34669         for(var i = 0; i < colCount; i++){
34670             if(cm.isHidden(i)){
34671                 this.hideColumn(i);
34672             }
34673             if(cm.config[i].align){
34674                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34675                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34676             }
34677         }*/
34678         
34679         this.updateHeaderSortState();
34680
34681         this.beforeInitialResize();
34682         this.layout(true);
34683
34684         // two part rendering gives faster view to the user
34685         this.renderPhase2.defer(1, this);
34686     },
34687
34688     renderPhase2 : function(){
34689         // render the rows now
34690         this.refresh();
34691         if(this.grid.autoSizeColumns){
34692             this.autoSizeColumns();
34693         }
34694     },
34695
34696     beforeInitialResize : function(){
34697
34698     },
34699
34700     onColumnSplitterMoved : function(i, w){
34701         this.userResized = true;
34702         var cm = this.grid.colModel;
34703         cm.setColumnWidth(i, w, true);
34704         var cid = cm.getColumnId(i);
34705         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34706         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34707         this.updateSplitters();
34708         this.layout();
34709         this.grid.fireEvent("columnresize", i, w);
34710     },
34711
34712     syncRowHeights : function(startIndex, endIndex){
34713         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34714             startIndex = startIndex || 0;
34715             var mrows = this.getBodyTable().rows;
34716             var lrows = this.getLockedTable().rows;
34717             var len = mrows.length-1;
34718             endIndex = Math.min(endIndex || len, len);
34719             for(var i = startIndex; i <= endIndex; i++){
34720                 var m = mrows[i], l = lrows[i];
34721                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34722                 m.style.height = l.style.height = h + "px";
34723             }
34724         }
34725     },
34726
34727     layout : function(initialRender, is2ndPass){
34728         var g = this.grid;
34729         var auto = g.autoHeight;
34730         var scrollOffset = 16;
34731         var c = g.getGridEl(), cm = this.cm,
34732                 expandCol = g.autoExpandColumn,
34733                 gv = this;
34734         //c.beginMeasure();
34735
34736         if(!c.dom.offsetWidth){ // display:none?
34737             if(initialRender){
34738                 this.lockedWrap.show();
34739                 this.mainWrap.show();
34740             }
34741             return;
34742         }
34743
34744         var hasLock = this.cm.isLocked(0);
34745
34746         var tbh = this.headerPanel.getHeight();
34747         var bbh = this.footerPanel.getHeight();
34748
34749         if(auto){
34750             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34751             var newHeight = ch + c.getBorderWidth("tb");
34752             if(g.maxHeight){
34753                 newHeight = Math.min(g.maxHeight, newHeight);
34754             }
34755             c.setHeight(newHeight);
34756         }
34757
34758         if(g.autoWidth){
34759             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34760         }
34761
34762         var s = this.scroller;
34763
34764         var csize = c.getSize(true);
34765
34766         this.el.setSize(csize.width, csize.height);
34767
34768         this.headerPanel.setWidth(csize.width);
34769         this.footerPanel.setWidth(csize.width);
34770
34771         var hdHeight = this.mainHd.getHeight();
34772         var vw = csize.width;
34773         var vh = csize.height - (tbh + bbh);
34774
34775         s.setSize(vw, vh);
34776
34777         var bt = this.getBodyTable();
34778         
34779         if(cm.getLockedCount() == cm.config.length){
34780             bt = this.getLockedTable();
34781         }
34782         
34783         var ltWidth = hasLock ?
34784                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34785
34786         var scrollHeight = bt.offsetHeight;
34787         var scrollWidth = ltWidth + bt.offsetWidth;
34788         var vscroll = false, hscroll = false;
34789
34790         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34791
34792         var lw = this.lockedWrap, mw = this.mainWrap;
34793         var lb = this.lockedBody, mb = this.mainBody;
34794
34795         setTimeout(function(){
34796             var t = s.dom.offsetTop;
34797             var w = s.dom.clientWidth,
34798                 h = s.dom.clientHeight;
34799
34800             lw.setTop(t);
34801             lw.setSize(ltWidth, h);
34802
34803             mw.setLeftTop(ltWidth, t);
34804             mw.setSize(w-ltWidth, h);
34805
34806             lb.setHeight(h-hdHeight);
34807             mb.setHeight(h-hdHeight);
34808
34809             if(is2ndPass !== true && !gv.userResized && expandCol){
34810                 // high speed resize without full column calculation
34811                 
34812                 var ci = cm.getIndexById(expandCol);
34813                 if (ci < 0) {
34814                     ci = cm.findColumnIndex(expandCol);
34815                 }
34816                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34817                 var expandId = cm.getColumnId(ci);
34818                 var  tw = cm.getTotalWidth(false);
34819                 var currentWidth = cm.getColumnWidth(ci);
34820                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34821                 if(currentWidth != cw){
34822                     cm.setColumnWidth(ci, cw, true);
34823                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34824                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34825                     gv.updateSplitters();
34826                     gv.layout(false, true);
34827                 }
34828             }
34829
34830             if(initialRender){
34831                 lw.show();
34832                 mw.show();
34833             }
34834             //c.endMeasure();
34835         }, 10);
34836     },
34837
34838     onWindowResize : function(){
34839         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34840             return;
34841         }
34842         this.layout();
34843     },
34844
34845     appendFooter : function(parentEl){
34846         return null;
34847     },
34848
34849     sortAscText : "Sort Ascending",
34850     sortDescText : "Sort Descending",
34851     lockText : "Lock Column",
34852     unlockText : "Unlock Column",
34853     columnsText : "Columns",
34854  
34855     columnsWiderText : "Wider",
34856     columnsNarrowText : "Thinner"
34857 });
34858
34859
34860 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34861     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34862     this.proxy.el.addClass('x-grid3-col-dd');
34863 };
34864
34865 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34866     handleMouseDown : function(e){
34867
34868     },
34869
34870     callHandleMouseDown : function(e){
34871         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34872     }
34873 });
34874 /*
34875  * Based on:
34876  * Ext JS Library 1.1.1
34877  * Copyright(c) 2006-2007, Ext JS, LLC.
34878  *
34879  * Originally Released Under LGPL - original licence link has changed is not relivant.
34880  *
34881  * Fork - LGPL
34882  * <script type="text/javascript">
34883  */
34884  
34885 // private
34886 // This is a support class used internally by the Grid components
34887 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34888     this.grid = grid;
34889     this.view = grid.getView();
34890     this.proxy = this.view.resizeProxy;
34891     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34892         "gridSplitters" + this.grid.getGridEl().id, {
34893         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34894     });
34895     this.setHandleElId(Roo.id(hd));
34896     this.setOuterHandleElId(Roo.id(hd2));
34897     this.scroll = false;
34898 };
34899 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34900     fly: Roo.Element.fly,
34901
34902     b4StartDrag : function(x, y){
34903         this.view.headersDisabled = true;
34904         this.proxy.setHeight(this.view.mainWrap.getHeight());
34905         var w = this.cm.getColumnWidth(this.cellIndex);
34906         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34907         this.resetConstraints();
34908         this.setXConstraint(minw, 1000);
34909         this.setYConstraint(0, 0);
34910         this.minX = x - minw;
34911         this.maxX = x + 1000;
34912         this.startPos = x;
34913         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34914     },
34915
34916
34917     handleMouseDown : function(e){
34918         ev = Roo.EventObject.setEvent(e);
34919         var t = this.fly(ev.getTarget());
34920         if(t.hasClass("x-grid-split")){
34921             this.cellIndex = this.view.getCellIndex(t.dom);
34922             this.split = t.dom;
34923             this.cm = this.grid.colModel;
34924             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34925                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34926             }
34927         }
34928     },
34929
34930     endDrag : function(e){
34931         this.view.headersDisabled = false;
34932         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34933         var diff = endX - this.startPos;
34934         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34935     },
34936
34937     autoOffset : function(){
34938         this.setDelta(0,0);
34939     }
34940 });/*
34941  * Based on:
34942  * Ext JS Library 1.1.1
34943  * Copyright(c) 2006-2007, Ext JS, LLC.
34944  *
34945  * Originally Released Under LGPL - original licence link has changed is not relivant.
34946  *
34947  * Fork - LGPL
34948  * <script type="text/javascript">
34949  */
34950  
34951 // private
34952 // This is a support class used internally by the Grid components
34953 Roo.grid.GridDragZone = function(grid, config){
34954     this.view = grid.getView();
34955     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34956     if(this.view.lockedBody){
34957         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34958         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34959     }
34960     this.scroll = false;
34961     this.grid = grid;
34962     this.ddel = document.createElement('div');
34963     this.ddel.className = 'x-grid-dd-wrap';
34964 };
34965
34966 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34967     ddGroup : "GridDD",
34968
34969     getDragData : function(e){
34970         var t = Roo.lib.Event.getTarget(e);
34971         var rowIndex = this.view.findRowIndex(t);
34972         var sm = this.grid.selModel;
34973             
34974         //Roo.log(rowIndex);
34975         
34976         if (sm.getSelectedCell) {
34977             // cell selection..
34978             if (!sm.getSelectedCell()) {
34979                 return false;
34980             }
34981             if (rowIndex != sm.getSelectedCell()[0]) {
34982                 return false;
34983             }
34984         
34985         }
34986         
34987         if(rowIndex !== false){
34988             
34989             // if editorgrid.. 
34990             
34991             
34992             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
34993                
34994             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34995               //  
34996             //}
34997             if (e.hasModifier()){
34998                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34999             }
35000             
35001             Roo.log("getDragData");
35002             
35003             return {
35004                 grid: this.grid,
35005                 ddel: this.ddel,
35006                 rowIndex: rowIndex,
35007                 selections:sm.getSelections ? sm.getSelections() : (
35008                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
35009                 )
35010             };
35011         }
35012         return false;
35013     },
35014
35015     onInitDrag : function(e){
35016         var data = this.dragData;
35017         this.ddel.innerHTML = this.grid.getDragDropText();
35018         this.proxy.update(this.ddel);
35019         // fire start drag?
35020     },
35021
35022     afterRepair : function(){
35023         this.dragging = false;
35024     },
35025
35026     getRepairXY : function(e, data){
35027         return false;
35028     },
35029
35030     onEndDrag : function(data, e){
35031         // fire end drag?
35032     },
35033
35034     onValidDrop : function(dd, e, id){
35035         // fire drag drop?
35036         this.hideProxy();
35037     },
35038
35039     beforeInvalidDrop : function(e, id){
35040
35041     }
35042 });/*
35043  * Based on:
35044  * Ext JS Library 1.1.1
35045  * Copyright(c) 2006-2007, Ext JS, LLC.
35046  *
35047  * Originally Released Under LGPL - original licence link has changed is not relivant.
35048  *
35049  * Fork - LGPL
35050  * <script type="text/javascript">
35051  */
35052  
35053
35054 /**
35055  * @class Roo.grid.ColumnModel
35056  * @extends Roo.util.Observable
35057  * This is the default implementation of a ColumnModel used by the Grid. It defines
35058  * the columns in the grid.
35059  * <br>Usage:<br>
35060  <pre><code>
35061  var colModel = new Roo.grid.ColumnModel([
35062         {header: "Ticker", width: 60, sortable: true, locked: true},
35063         {header: "Company Name", width: 150, sortable: true},
35064         {header: "Market Cap.", width: 100, sortable: true},
35065         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35066         {header: "Employees", width: 100, sortable: true, resizable: false}
35067  ]);
35068  </code></pre>
35069  * <p>
35070  
35071  * The config options listed for this class are options which may appear in each
35072  * individual column definition.
35073  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35074  * @constructor
35075  * @param {Object} config An Array of column config objects. See this class's
35076  * config objects for details.
35077 */
35078 Roo.grid.ColumnModel = function(config){
35079         /**
35080      * The config passed into the constructor
35081      */
35082     this.config = config;
35083     this.lookup = {};
35084
35085     // if no id, create one
35086     // if the column does not have a dataIndex mapping,
35087     // map it to the order it is in the config
35088     for(var i = 0, len = config.length; i < len; i++){
35089         var c = config[i];
35090         if(typeof c.dataIndex == "undefined"){
35091             c.dataIndex = i;
35092         }
35093         if(typeof c.renderer == "string"){
35094             c.renderer = Roo.util.Format[c.renderer];
35095         }
35096         if(typeof c.id == "undefined"){
35097             c.id = Roo.id();
35098         }
35099         if(c.editor && c.editor.xtype){
35100             c.editor  = Roo.factory(c.editor, Roo.grid);
35101         }
35102         if(c.editor && c.editor.isFormField){
35103             c.editor = new Roo.grid.GridEditor(c.editor);
35104         }
35105         this.lookup[c.id] = c;
35106     }
35107
35108     /**
35109      * The width of columns which have no width specified (defaults to 100)
35110      * @type Number
35111      */
35112     this.defaultWidth = 100;
35113
35114     /**
35115      * Default sortable of columns which have no sortable specified (defaults to false)
35116      * @type Boolean
35117      */
35118     this.defaultSortable = false;
35119
35120     this.addEvents({
35121         /**
35122              * @event widthchange
35123              * Fires when the width of a column changes.
35124              * @param {ColumnModel} this
35125              * @param {Number} columnIndex The column index
35126              * @param {Number} newWidth The new width
35127              */
35128             "widthchange": true,
35129         /**
35130              * @event headerchange
35131              * Fires when the text of a header changes.
35132              * @param {ColumnModel} this
35133              * @param {Number} columnIndex The column index
35134              * @param {Number} newText The new header text
35135              */
35136             "headerchange": true,
35137         /**
35138              * @event hiddenchange
35139              * Fires when a column is hidden or "unhidden".
35140              * @param {ColumnModel} this
35141              * @param {Number} columnIndex The column index
35142              * @param {Boolean} hidden true if hidden, false otherwise
35143              */
35144             "hiddenchange": true,
35145             /**
35146          * @event columnmoved
35147          * Fires when a column is moved.
35148          * @param {ColumnModel} this
35149          * @param {Number} oldIndex
35150          * @param {Number} newIndex
35151          */
35152         "columnmoved" : true,
35153         /**
35154          * @event columlockchange
35155          * Fires when a column's locked state is changed
35156          * @param {ColumnModel} this
35157          * @param {Number} colIndex
35158          * @param {Boolean} locked true if locked
35159          */
35160         "columnlockchange" : true
35161     });
35162     Roo.grid.ColumnModel.superclass.constructor.call(this);
35163 };
35164 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35165     /**
35166      * @cfg {String} header The header text to display in the Grid view.
35167      */
35168     /**
35169      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35170      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35171      * specified, the column's index is used as an index into the Record's data Array.
35172      */
35173     /**
35174      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35175      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35176      */
35177     /**
35178      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35179      * Defaults to the value of the {@link #defaultSortable} property.
35180      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35181      */
35182     /**
35183      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35184      */
35185     /**
35186      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35187      */
35188     /**
35189      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35190      */
35191     /**
35192      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35193      */
35194     /**
35195      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35196      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35197      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35198      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35199      */
35200        /**
35201      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35202      */
35203     /**
35204      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35205      */
35206     /**
35207      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35208      */
35209     /**
35210      * @cfg {String} cursor (Optional)
35211      */
35212     /**
35213      * @cfg {String} tooltip (Optional)
35214      */
35215     /**
35216      * @cfg {Number} xs (Optional)
35217      */
35218     /**
35219      * @cfg {Number} sm (Optional)
35220      */
35221     /**
35222      * @cfg {Number} md (Optional)
35223      */
35224     /**
35225      * @cfg {Number} lg (Optional)
35226      */
35227     /**
35228      * Returns the id of the column at the specified index.
35229      * @param {Number} index The column index
35230      * @return {String} the id
35231      */
35232     getColumnId : function(index){
35233         return this.config[index].id;
35234     },
35235
35236     /**
35237      * Returns the column for a specified id.
35238      * @param {String} id The column id
35239      * @return {Object} the column
35240      */
35241     getColumnById : function(id){
35242         return this.lookup[id];
35243     },
35244
35245     
35246     /**
35247      * Returns the column for a specified dataIndex.
35248      * @param {String} dataIndex The column dataIndex
35249      * @return {Object|Boolean} the column or false if not found
35250      */
35251     getColumnByDataIndex: function(dataIndex){
35252         var index = this.findColumnIndex(dataIndex);
35253         return index > -1 ? this.config[index] : false;
35254     },
35255     
35256     /**
35257      * Returns the index for a specified column id.
35258      * @param {String} id The column id
35259      * @return {Number} the index, or -1 if not found
35260      */
35261     getIndexById : function(id){
35262         for(var i = 0, len = this.config.length; i < len; i++){
35263             if(this.config[i].id == id){
35264                 return i;
35265             }
35266         }
35267         return -1;
35268     },
35269     
35270     /**
35271      * Returns the index for a specified column dataIndex.
35272      * @param {String} dataIndex The column dataIndex
35273      * @return {Number} the index, or -1 if not found
35274      */
35275     
35276     findColumnIndex : function(dataIndex){
35277         for(var i = 0, len = this.config.length; i < len; i++){
35278             if(this.config[i].dataIndex == dataIndex){
35279                 return i;
35280             }
35281         }
35282         return -1;
35283     },
35284     
35285     
35286     moveColumn : function(oldIndex, newIndex){
35287         var c = this.config[oldIndex];
35288         this.config.splice(oldIndex, 1);
35289         this.config.splice(newIndex, 0, c);
35290         this.dataMap = null;
35291         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35292     },
35293
35294     isLocked : function(colIndex){
35295         return this.config[colIndex].locked === true;
35296     },
35297
35298     setLocked : function(colIndex, value, suppressEvent){
35299         if(this.isLocked(colIndex) == value){
35300             return;
35301         }
35302         this.config[colIndex].locked = value;
35303         if(!suppressEvent){
35304             this.fireEvent("columnlockchange", this, colIndex, value);
35305         }
35306     },
35307
35308     getTotalLockedWidth : function(){
35309         var totalWidth = 0;
35310         for(var i = 0; i < this.config.length; i++){
35311             if(this.isLocked(i) && !this.isHidden(i)){
35312                 this.totalWidth += this.getColumnWidth(i);
35313             }
35314         }
35315         return totalWidth;
35316     },
35317
35318     getLockedCount : function(){
35319         for(var i = 0, len = this.config.length; i < len; i++){
35320             if(!this.isLocked(i)){
35321                 return i;
35322             }
35323         }
35324         
35325         return this.config.length;
35326     },
35327
35328     /**
35329      * Returns the number of columns.
35330      * @return {Number}
35331      */
35332     getColumnCount : function(visibleOnly){
35333         if(visibleOnly === true){
35334             var c = 0;
35335             for(var i = 0, len = this.config.length; i < len; i++){
35336                 if(!this.isHidden(i)){
35337                     c++;
35338                 }
35339             }
35340             return c;
35341         }
35342         return this.config.length;
35343     },
35344
35345     /**
35346      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35347      * @param {Function} fn
35348      * @param {Object} scope (optional)
35349      * @return {Array} result
35350      */
35351     getColumnsBy : function(fn, scope){
35352         var r = [];
35353         for(var i = 0, len = this.config.length; i < len; i++){
35354             var c = this.config[i];
35355             if(fn.call(scope||this, c, i) === true){
35356                 r[r.length] = c;
35357             }
35358         }
35359         return r;
35360     },
35361
35362     /**
35363      * Returns true if the specified column is sortable.
35364      * @param {Number} col The column index
35365      * @return {Boolean}
35366      */
35367     isSortable : function(col){
35368         if(typeof this.config[col].sortable == "undefined"){
35369             return this.defaultSortable;
35370         }
35371         return this.config[col].sortable;
35372     },
35373
35374     /**
35375      * Returns the rendering (formatting) function defined for the column.
35376      * @param {Number} col The column index.
35377      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35378      */
35379     getRenderer : function(col){
35380         if(!this.config[col].renderer){
35381             return Roo.grid.ColumnModel.defaultRenderer;
35382         }
35383         return this.config[col].renderer;
35384     },
35385
35386     /**
35387      * Sets the rendering (formatting) function for a column.
35388      * @param {Number} col The column index
35389      * @param {Function} fn The function to use to process the cell's raw data
35390      * to return HTML markup for the grid view. The render function is called with
35391      * the following parameters:<ul>
35392      * <li>Data value.</li>
35393      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35394      * <li>css A CSS style string to apply to the table cell.</li>
35395      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35396      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35397      * <li>Row index</li>
35398      * <li>Column index</li>
35399      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35400      */
35401     setRenderer : function(col, fn){
35402         this.config[col].renderer = fn;
35403     },
35404
35405     /**
35406      * Returns the width for the specified column.
35407      * @param {Number} col The column index
35408      * @return {Number}
35409      */
35410     getColumnWidth : function(col){
35411         return this.config[col].width * 1 || this.defaultWidth;
35412     },
35413
35414     /**
35415      * Sets the width for a column.
35416      * @param {Number} col The column index
35417      * @param {Number} width The new width
35418      */
35419     setColumnWidth : function(col, width, suppressEvent){
35420         this.config[col].width = width;
35421         this.totalWidth = null;
35422         if(!suppressEvent){
35423              this.fireEvent("widthchange", this, col, width);
35424         }
35425     },
35426
35427     /**
35428      * Returns the total width of all columns.
35429      * @param {Boolean} includeHidden True to include hidden column widths
35430      * @return {Number}
35431      */
35432     getTotalWidth : function(includeHidden){
35433         if(!this.totalWidth){
35434             this.totalWidth = 0;
35435             for(var i = 0, len = this.config.length; i < len; i++){
35436                 if(includeHidden || !this.isHidden(i)){
35437                     this.totalWidth += this.getColumnWidth(i);
35438                 }
35439             }
35440         }
35441         return this.totalWidth;
35442     },
35443
35444     /**
35445      * Returns the header for the specified column.
35446      * @param {Number} col The column index
35447      * @return {String}
35448      */
35449     getColumnHeader : function(col){
35450         return this.config[col].header;
35451     },
35452
35453     /**
35454      * Sets the header for a column.
35455      * @param {Number} col The column index
35456      * @param {String} header The new header
35457      */
35458     setColumnHeader : function(col, header){
35459         this.config[col].header = header;
35460         this.fireEvent("headerchange", this, col, header);
35461     },
35462
35463     /**
35464      * Returns the tooltip for the specified column.
35465      * @param {Number} col The column index
35466      * @return {String}
35467      */
35468     getColumnTooltip : function(col){
35469             return this.config[col].tooltip;
35470     },
35471     /**
35472      * Sets the tooltip for a column.
35473      * @param {Number} col The column index
35474      * @param {String} tooltip The new tooltip
35475      */
35476     setColumnTooltip : function(col, tooltip){
35477             this.config[col].tooltip = tooltip;
35478     },
35479
35480     /**
35481      * Returns the dataIndex for the specified column.
35482      * @param {Number} col The column index
35483      * @return {Number}
35484      */
35485     getDataIndex : function(col){
35486         return this.config[col].dataIndex;
35487     },
35488
35489     /**
35490      * Sets the dataIndex for a column.
35491      * @param {Number} col The column index
35492      * @param {Number} dataIndex The new dataIndex
35493      */
35494     setDataIndex : function(col, dataIndex){
35495         this.config[col].dataIndex = dataIndex;
35496     },
35497
35498     
35499     
35500     /**
35501      * Returns true if the cell is editable.
35502      * @param {Number} colIndex The column index
35503      * @param {Number} rowIndex The row index - this is nto actually used..?
35504      * @return {Boolean}
35505      */
35506     isCellEditable : function(colIndex, rowIndex){
35507         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35508     },
35509
35510     /**
35511      * Returns the editor defined for the cell/column.
35512      * return false or null to disable editing.
35513      * @param {Number} colIndex The column index
35514      * @param {Number} rowIndex The row index
35515      * @return {Object}
35516      */
35517     getCellEditor : function(colIndex, rowIndex){
35518         return this.config[colIndex].editor;
35519     },
35520
35521     /**
35522      * Sets if a column is editable.
35523      * @param {Number} col The column index
35524      * @param {Boolean} editable True if the column is editable
35525      */
35526     setEditable : function(col, editable){
35527         this.config[col].editable = editable;
35528     },
35529
35530
35531     /**
35532      * Returns true if the column is hidden.
35533      * @param {Number} colIndex The column index
35534      * @return {Boolean}
35535      */
35536     isHidden : function(colIndex){
35537         return this.config[colIndex].hidden;
35538     },
35539
35540
35541     /**
35542      * Returns true if the column width cannot be changed
35543      */
35544     isFixed : function(colIndex){
35545         return this.config[colIndex].fixed;
35546     },
35547
35548     /**
35549      * Returns true if the column can be resized
35550      * @return {Boolean}
35551      */
35552     isResizable : function(colIndex){
35553         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35554     },
35555     /**
35556      * Sets if a column is hidden.
35557      * @param {Number} colIndex The column index
35558      * @param {Boolean} hidden True if the column is hidden
35559      */
35560     setHidden : function(colIndex, hidden){
35561         this.config[colIndex].hidden = hidden;
35562         this.totalWidth = null;
35563         this.fireEvent("hiddenchange", this, colIndex, hidden);
35564     },
35565
35566     /**
35567      * Sets the editor for a column.
35568      * @param {Number} col The column index
35569      * @param {Object} editor The editor object
35570      */
35571     setEditor : function(col, editor){
35572         this.config[col].editor = editor;
35573     }
35574 });
35575
35576 Roo.grid.ColumnModel.defaultRenderer = function(value)
35577 {
35578     if(typeof value == "object") {
35579         return value;
35580     }
35581         if(typeof value == "string" && value.length < 1){
35582             return "&#160;";
35583         }
35584     
35585         return String.format("{0}", value);
35586 };
35587
35588 // Alias for backwards compatibility
35589 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35590 /*
35591  * Based on:
35592  * Ext JS Library 1.1.1
35593  * Copyright(c) 2006-2007, Ext JS, LLC.
35594  *
35595  * Originally Released Under LGPL - original licence link has changed is not relivant.
35596  *
35597  * Fork - LGPL
35598  * <script type="text/javascript">
35599  */
35600
35601 /**
35602  * @class Roo.grid.AbstractSelectionModel
35603  * @extends Roo.util.Observable
35604  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35605  * implemented by descendant classes.  This class should not be directly instantiated.
35606  * @constructor
35607  */
35608 Roo.grid.AbstractSelectionModel = function(){
35609     this.locked = false;
35610     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35611 };
35612
35613 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35614     /** @ignore Called by the grid automatically. Do not call directly. */
35615     init : function(grid){
35616         this.grid = grid;
35617         this.initEvents();
35618     },
35619
35620     /**
35621      * Locks the selections.
35622      */
35623     lock : function(){
35624         this.locked = true;
35625     },
35626
35627     /**
35628      * Unlocks the selections.
35629      */
35630     unlock : function(){
35631         this.locked = false;
35632     },
35633
35634     /**
35635      * Returns true if the selections are locked.
35636      * @return {Boolean}
35637      */
35638     isLocked : function(){
35639         return this.locked;
35640     }
35641 });/*
35642  * Based on:
35643  * Ext JS Library 1.1.1
35644  * Copyright(c) 2006-2007, Ext JS, LLC.
35645  *
35646  * Originally Released Under LGPL - original licence link has changed is not relivant.
35647  *
35648  * Fork - LGPL
35649  * <script type="text/javascript">
35650  */
35651 /**
35652  * @extends Roo.grid.AbstractSelectionModel
35653  * @class Roo.grid.RowSelectionModel
35654  * The default SelectionModel used by {@link Roo.grid.Grid}.
35655  * It supports multiple selections and keyboard selection/navigation. 
35656  * @constructor
35657  * @param {Object} config
35658  */
35659 Roo.grid.RowSelectionModel = function(config){
35660     Roo.apply(this, config);
35661     this.selections = new Roo.util.MixedCollection(false, function(o){
35662         return o.id;
35663     });
35664
35665     this.last = false;
35666     this.lastActive = false;
35667
35668     this.addEvents({
35669         /**
35670              * @event selectionchange
35671              * Fires when the selection changes
35672              * @param {SelectionModel} this
35673              */
35674             "selectionchange" : true,
35675         /**
35676              * @event afterselectionchange
35677              * Fires after the selection changes (eg. by key press or clicking)
35678              * @param {SelectionModel} this
35679              */
35680             "afterselectionchange" : true,
35681         /**
35682              * @event beforerowselect
35683              * Fires when a row is selected being selected, return false to cancel.
35684              * @param {SelectionModel} this
35685              * @param {Number} rowIndex The selected index
35686              * @param {Boolean} keepExisting False if other selections will be cleared
35687              */
35688             "beforerowselect" : true,
35689         /**
35690              * @event rowselect
35691              * Fires when a row is selected.
35692              * @param {SelectionModel} this
35693              * @param {Number} rowIndex The selected index
35694              * @param {Roo.data.Record} r The record
35695              */
35696             "rowselect" : true,
35697         /**
35698              * @event rowdeselect
35699              * Fires when a row is deselected.
35700              * @param {SelectionModel} this
35701              * @param {Number} rowIndex The selected index
35702              */
35703         "rowdeselect" : true
35704     });
35705     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35706     this.locked = false;
35707 };
35708
35709 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35710     /**
35711      * @cfg {Boolean} singleSelect
35712      * True to allow selection of only one row at a time (defaults to false)
35713      */
35714     singleSelect : false,
35715
35716     // private
35717     initEvents : function(){
35718
35719         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35720             this.grid.on("mousedown", this.handleMouseDown, this);
35721         }else{ // allow click to work like normal
35722             this.grid.on("rowclick", this.handleDragableRowClick, this);
35723         }
35724
35725         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35726             "up" : function(e){
35727                 if(!e.shiftKey){
35728                     this.selectPrevious(e.shiftKey);
35729                 }else if(this.last !== false && this.lastActive !== false){
35730                     var last = this.last;
35731                     this.selectRange(this.last,  this.lastActive-1);
35732                     this.grid.getView().focusRow(this.lastActive);
35733                     if(last !== false){
35734                         this.last = last;
35735                     }
35736                 }else{
35737                     this.selectFirstRow();
35738                 }
35739                 this.fireEvent("afterselectionchange", this);
35740             },
35741             "down" : function(e){
35742                 if(!e.shiftKey){
35743                     this.selectNext(e.shiftKey);
35744                 }else if(this.last !== false && this.lastActive !== false){
35745                     var last = this.last;
35746                     this.selectRange(this.last,  this.lastActive+1);
35747                     this.grid.getView().focusRow(this.lastActive);
35748                     if(last !== false){
35749                         this.last = last;
35750                     }
35751                 }else{
35752                     this.selectFirstRow();
35753                 }
35754                 this.fireEvent("afterselectionchange", this);
35755             },
35756             scope: this
35757         });
35758
35759         var view = this.grid.view;
35760         view.on("refresh", this.onRefresh, this);
35761         view.on("rowupdated", this.onRowUpdated, this);
35762         view.on("rowremoved", this.onRemove, this);
35763     },
35764
35765     // private
35766     onRefresh : function(){
35767         var ds = this.grid.dataSource, i, v = this.grid.view;
35768         var s = this.selections;
35769         s.each(function(r){
35770             if((i = ds.indexOfId(r.id)) != -1){
35771                 v.onRowSelect(i);
35772                 s.add(ds.getAt(i)); // updating the selection relate data
35773             }else{
35774                 s.remove(r);
35775             }
35776         });
35777     },
35778
35779     // private
35780     onRemove : function(v, index, r){
35781         this.selections.remove(r);
35782     },
35783
35784     // private
35785     onRowUpdated : function(v, index, r){
35786         if(this.isSelected(r)){
35787             v.onRowSelect(index);
35788         }
35789     },
35790
35791     /**
35792      * Select records.
35793      * @param {Array} records The records to select
35794      * @param {Boolean} keepExisting (optional) True to keep existing selections
35795      */
35796     selectRecords : function(records, keepExisting){
35797         if(!keepExisting){
35798             this.clearSelections();
35799         }
35800         var ds = this.grid.dataSource;
35801         for(var i = 0, len = records.length; i < len; i++){
35802             this.selectRow(ds.indexOf(records[i]), true);
35803         }
35804     },
35805
35806     /**
35807      * Gets the number of selected rows.
35808      * @return {Number}
35809      */
35810     getCount : function(){
35811         return this.selections.length;
35812     },
35813
35814     /**
35815      * Selects the first row in the grid.
35816      */
35817     selectFirstRow : function(){
35818         this.selectRow(0);
35819     },
35820
35821     /**
35822      * Select the last row.
35823      * @param {Boolean} keepExisting (optional) True to keep existing selections
35824      */
35825     selectLastRow : function(keepExisting){
35826         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35827     },
35828
35829     /**
35830      * Selects the row immediately following the last selected row.
35831      * @param {Boolean} keepExisting (optional) True to keep existing selections
35832      */
35833     selectNext : function(keepExisting){
35834         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35835             this.selectRow(this.last+1, keepExisting);
35836             this.grid.getView().focusRow(this.last);
35837         }
35838     },
35839
35840     /**
35841      * Selects the row that precedes the last selected row.
35842      * @param {Boolean} keepExisting (optional) True to keep existing selections
35843      */
35844     selectPrevious : function(keepExisting){
35845         if(this.last){
35846             this.selectRow(this.last-1, keepExisting);
35847             this.grid.getView().focusRow(this.last);
35848         }
35849     },
35850
35851     /**
35852      * Returns the selected records
35853      * @return {Array} Array of selected records
35854      */
35855     getSelections : function(){
35856         return [].concat(this.selections.items);
35857     },
35858
35859     /**
35860      * Returns the first selected record.
35861      * @return {Record}
35862      */
35863     getSelected : function(){
35864         return this.selections.itemAt(0);
35865     },
35866
35867
35868     /**
35869      * Clears all selections.
35870      */
35871     clearSelections : function(fast){
35872         if(this.locked) {
35873             return;
35874         }
35875         if(fast !== true){
35876             var ds = this.grid.dataSource;
35877             var s = this.selections;
35878             s.each(function(r){
35879                 this.deselectRow(ds.indexOfId(r.id));
35880             }, this);
35881             s.clear();
35882         }else{
35883             this.selections.clear();
35884         }
35885         this.last = false;
35886     },
35887
35888
35889     /**
35890      * Selects all rows.
35891      */
35892     selectAll : function(){
35893         if(this.locked) {
35894             return;
35895         }
35896         this.selections.clear();
35897         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35898             this.selectRow(i, true);
35899         }
35900     },
35901
35902     /**
35903      * Returns True if there is a selection.
35904      * @return {Boolean}
35905      */
35906     hasSelection : function(){
35907         return this.selections.length > 0;
35908     },
35909
35910     /**
35911      * Returns True if the specified row is selected.
35912      * @param {Number/Record} record The record or index of the record to check
35913      * @return {Boolean}
35914      */
35915     isSelected : function(index){
35916         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35917         return (r && this.selections.key(r.id) ? true : false);
35918     },
35919
35920     /**
35921      * Returns True if the specified record id is selected.
35922      * @param {String} id The id of record to check
35923      * @return {Boolean}
35924      */
35925     isIdSelected : function(id){
35926         return (this.selections.key(id) ? true : false);
35927     },
35928
35929     // private
35930     handleMouseDown : function(e, t){
35931         var view = this.grid.getView(), rowIndex;
35932         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35933             return;
35934         };
35935         if(e.shiftKey && this.last !== false){
35936             var last = this.last;
35937             this.selectRange(last, rowIndex, e.ctrlKey);
35938             this.last = last; // reset the last
35939             view.focusRow(rowIndex);
35940         }else{
35941             var isSelected = this.isSelected(rowIndex);
35942             if(e.button !== 0 && isSelected){
35943                 view.focusRow(rowIndex);
35944             }else if(e.ctrlKey && isSelected){
35945                 this.deselectRow(rowIndex);
35946             }else if(!isSelected){
35947                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35948                 view.focusRow(rowIndex);
35949             }
35950         }
35951         this.fireEvent("afterselectionchange", this);
35952     },
35953     // private
35954     handleDragableRowClick :  function(grid, rowIndex, e) 
35955     {
35956         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35957             this.selectRow(rowIndex, false);
35958             grid.view.focusRow(rowIndex);
35959              this.fireEvent("afterselectionchange", this);
35960         }
35961     },
35962     
35963     /**
35964      * Selects multiple rows.
35965      * @param {Array} rows Array of the indexes of the row to select
35966      * @param {Boolean} keepExisting (optional) True to keep existing selections
35967      */
35968     selectRows : function(rows, keepExisting){
35969         if(!keepExisting){
35970             this.clearSelections();
35971         }
35972         for(var i = 0, len = rows.length; i < len; i++){
35973             this.selectRow(rows[i], true);
35974         }
35975     },
35976
35977     /**
35978      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35979      * @param {Number} startRow The index of the first row in the range
35980      * @param {Number} endRow The index of the last row in the range
35981      * @param {Boolean} keepExisting (optional) True to retain existing selections
35982      */
35983     selectRange : function(startRow, endRow, keepExisting){
35984         if(this.locked) {
35985             return;
35986         }
35987         if(!keepExisting){
35988             this.clearSelections();
35989         }
35990         if(startRow <= endRow){
35991             for(var i = startRow; i <= endRow; i++){
35992                 this.selectRow(i, true);
35993             }
35994         }else{
35995             for(var i = startRow; i >= endRow; i--){
35996                 this.selectRow(i, true);
35997             }
35998         }
35999     },
36000
36001     /**
36002      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36003      * @param {Number} startRow The index of the first row in the range
36004      * @param {Number} endRow The index of the last row in the range
36005      */
36006     deselectRange : function(startRow, endRow, preventViewNotify){
36007         if(this.locked) {
36008             return;
36009         }
36010         for(var i = startRow; i <= endRow; i++){
36011             this.deselectRow(i, preventViewNotify);
36012         }
36013     },
36014
36015     /**
36016      * Selects a row.
36017      * @param {Number} row The index of the row to select
36018      * @param {Boolean} keepExisting (optional) True to keep existing selections
36019      */
36020     selectRow : function(index, keepExisting, preventViewNotify){
36021         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
36022             return;
36023         }
36024         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36025             if(!keepExisting || this.singleSelect){
36026                 this.clearSelections();
36027             }
36028             var r = this.grid.dataSource.getAt(index);
36029             this.selections.add(r);
36030             this.last = this.lastActive = index;
36031             if(!preventViewNotify){
36032                 this.grid.getView().onRowSelect(index);
36033             }
36034             this.fireEvent("rowselect", this, index, r);
36035             this.fireEvent("selectionchange", this);
36036         }
36037     },
36038
36039     /**
36040      * Deselects a row.
36041      * @param {Number} row The index of the row to deselect
36042      */
36043     deselectRow : function(index, preventViewNotify){
36044         if(this.locked) {
36045             return;
36046         }
36047         if(this.last == index){
36048             this.last = false;
36049         }
36050         if(this.lastActive == index){
36051             this.lastActive = false;
36052         }
36053         var r = this.grid.dataSource.getAt(index);
36054         this.selections.remove(r);
36055         if(!preventViewNotify){
36056             this.grid.getView().onRowDeselect(index);
36057         }
36058         this.fireEvent("rowdeselect", this, index);
36059         this.fireEvent("selectionchange", this);
36060     },
36061
36062     // private
36063     restoreLast : function(){
36064         if(this._last){
36065             this.last = this._last;
36066         }
36067     },
36068
36069     // private
36070     acceptsNav : function(row, col, cm){
36071         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36072     },
36073
36074     // private
36075     onEditorKey : function(field, e){
36076         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36077         if(k == e.TAB){
36078             e.stopEvent();
36079             ed.completeEdit();
36080             if(e.shiftKey){
36081                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36082             }else{
36083                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36084             }
36085         }else if(k == e.ENTER && !e.ctrlKey){
36086             e.stopEvent();
36087             ed.completeEdit();
36088             if(e.shiftKey){
36089                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36090             }else{
36091                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36092             }
36093         }else if(k == e.ESC){
36094             ed.cancelEdit();
36095         }
36096         if(newCell){
36097             g.startEditing(newCell[0], newCell[1]);
36098         }
36099     }
36100 });/*
36101  * Based on:
36102  * Ext JS Library 1.1.1
36103  * Copyright(c) 2006-2007, Ext JS, LLC.
36104  *
36105  * Originally Released Under LGPL - original licence link has changed is not relivant.
36106  *
36107  * Fork - LGPL
36108  * <script type="text/javascript">
36109  */
36110 /**
36111  * @class Roo.grid.CellSelectionModel
36112  * @extends Roo.grid.AbstractSelectionModel
36113  * This class provides the basic implementation for cell selection in a grid.
36114  * @constructor
36115  * @param {Object} config The object containing the configuration of this model.
36116  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36117  */
36118 Roo.grid.CellSelectionModel = function(config){
36119     Roo.apply(this, config);
36120
36121     this.selection = null;
36122
36123     this.addEvents({
36124         /**
36125              * @event beforerowselect
36126              * Fires before a cell is selected.
36127              * @param {SelectionModel} this
36128              * @param {Number} rowIndex The selected row index
36129              * @param {Number} colIndex The selected cell index
36130              */
36131             "beforecellselect" : true,
36132         /**
36133              * @event cellselect
36134              * Fires when a cell is selected.
36135              * @param {SelectionModel} this
36136              * @param {Number} rowIndex The selected row index
36137              * @param {Number} colIndex The selected cell index
36138              */
36139             "cellselect" : true,
36140         /**
36141              * @event selectionchange
36142              * Fires when the active selection changes.
36143              * @param {SelectionModel} this
36144              * @param {Object} selection null for no selection or an object (o) with two properties
36145                 <ul>
36146                 <li>o.record: the record object for the row the selection is in</li>
36147                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36148                 </ul>
36149              */
36150             "selectionchange" : true,
36151         /**
36152              * @event tabend
36153              * Fires when the tab (or enter) was pressed on the last editable cell
36154              * You can use this to trigger add new row.
36155              * @param {SelectionModel} this
36156              */
36157             "tabend" : true,
36158          /**
36159              * @event beforeeditnext
36160              * Fires before the next editable sell is made active
36161              * You can use this to skip to another cell or fire the tabend
36162              *    if you set cell to false
36163              * @param {Object} eventdata object : { cell : [ row, col ] } 
36164              */
36165             "beforeeditnext" : true
36166     });
36167     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36168 };
36169
36170 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36171     
36172     enter_is_tab: false,
36173
36174     /** @ignore */
36175     initEvents : function(){
36176         this.grid.on("mousedown", this.handleMouseDown, this);
36177         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36178         var view = this.grid.view;
36179         view.on("refresh", this.onViewChange, this);
36180         view.on("rowupdated", this.onRowUpdated, this);
36181         view.on("beforerowremoved", this.clearSelections, this);
36182         view.on("beforerowsinserted", this.clearSelections, this);
36183         if(this.grid.isEditor){
36184             this.grid.on("beforeedit", this.beforeEdit,  this);
36185         }
36186     },
36187
36188         //private
36189     beforeEdit : function(e){
36190         this.select(e.row, e.column, false, true, e.record);
36191     },
36192
36193         //private
36194     onRowUpdated : function(v, index, r){
36195         if(this.selection && this.selection.record == r){
36196             v.onCellSelect(index, this.selection.cell[1]);
36197         }
36198     },
36199
36200         //private
36201     onViewChange : function(){
36202         this.clearSelections(true);
36203     },
36204
36205         /**
36206          * Returns the currently selected cell,.
36207          * @return {Array} The selected cell (row, column) or null if none selected.
36208          */
36209     getSelectedCell : function(){
36210         return this.selection ? this.selection.cell : null;
36211     },
36212
36213     /**
36214      * Clears all selections.
36215      * @param {Boolean} true to prevent the gridview from being notified about the change.
36216      */
36217     clearSelections : function(preventNotify){
36218         var s = this.selection;
36219         if(s){
36220             if(preventNotify !== true){
36221                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36222             }
36223             this.selection = null;
36224             this.fireEvent("selectionchange", this, null);
36225         }
36226     },
36227
36228     /**
36229      * Returns true if there is a selection.
36230      * @return {Boolean}
36231      */
36232     hasSelection : function(){
36233         return this.selection ? true : false;
36234     },
36235
36236     /** @ignore */
36237     handleMouseDown : function(e, t){
36238         var v = this.grid.getView();
36239         if(this.isLocked()){
36240             return;
36241         };
36242         var row = v.findRowIndex(t);
36243         var cell = v.findCellIndex(t);
36244         if(row !== false && cell !== false){
36245             this.select(row, cell);
36246         }
36247     },
36248
36249     /**
36250      * Selects a cell.
36251      * @param {Number} rowIndex
36252      * @param {Number} collIndex
36253      */
36254     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36255         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36256             this.clearSelections();
36257             r = r || this.grid.dataSource.getAt(rowIndex);
36258             this.selection = {
36259                 record : r,
36260                 cell : [rowIndex, colIndex]
36261             };
36262             if(!preventViewNotify){
36263                 var v = this.grid.getView();
36264                 v.onCellSelect(rowIndex, colIndex);
36265                 if(preventFocus !== true){
36266                     v.focusCell(rowIndex, colIndex);
36267                 }
36268             }
36269             this.fireEvent("cellselect", this, rowIndex, colIndex);
36270             this.fireEvent("selectionchange", this, this.selection);
36271         }
36272     },
36273
36274         //private
36275     isSelectable : function(rowIndex, colIndex, cm){
36276         return !cm.isHidden(colIndex);
36277     },
36278
36279     /** @ignore */
36280     handleKeyDown : function(e){
36281         //Roo.log('Cell Sel Model handleKeyDown');
36282         if(!e.isNavKeyPress()){
36283             return;
36284         }
36285         var g = this.grid, s = this.selection;
36286         if(!s){
36287             e.stopEvent();
36288             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36289             if(cell){
36290                 this.select(cell[0], cell[1]);
36291             }
36292             return;
36293         }
36294         var sm = this;
36295         var walk = function(row, col, step){
36296             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36297         };
36298         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36299         var newCell;
36300
36301       
36302
36303         switch(k){
36304             case e.TAB:
36305                 // handled by onEditorKey
36306                 if (g.isEditor && g.editing) {
36307                     return;
36308                 }
36309                 if(e.shiftKey) {
36310                     newCell = walk(r, c-1, -1);
36311                 } else {
36312                     newCell = walk(r, c+1, 1);
36313                 }
36314                 break;
36315             
36316             case e.DOWN:
36317                newCell = walk(r+1, c, 1);
36318                 break;
36319             
36320             case e.UP:
36321                 newCell = walk(r-1, c, -1);
36322                 break;
36323             
36324             case e.RIGHT:
36325                 newCell = walk(r, c+1, 1);
36326                 break;
36327             
36328             case e.LEFT:
36329                 newCell = walk(r, c-1, -1);
36330                 break;
36331             
36332             case e.ENTER:
36333                 
36334                 if(g.isEditor && !g.editing){
36335                    g.startEditing(r, c);
36336                    e.stopEvent();
36337                    return;
36338                 }
36339                 
36340                 
36341              break;
36342         };
36343         if(newCell){
36344             this.select(newCell[0], newCell[1]);
36345             e.stopEvent();
36346             
36347         }
36348     },
36349
36350     acceptsNav : function(row, col, cm){
36351         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36352     },
36353     /**
36354      * Selects a cell.
36355      * @param {Number} field (not used) - as it's normally used as a listener
36356      * @param {Number} e - event - fake it by using
36357      *
36358      * var e = Roo.EventObjectImpl.prototype;
36359      * e.keyCode = e.TAB
36360      *
36361      * 
36362      */
36363     onEditorKey : function(field, e){
36364         
36365         var k = e.getKey(),
36366             newCell,
36367             g = this.grid,
36368             ed = g.activeEditor,
36369             forward = false;
36370         ///Roo.log('onEditorKey' + k);
36371         
36372         
36373         if (this.enter_is_tab && k == e.ENTER) {
36374             k = e.TAB;
36375         }
36376         
36377         if(k == e.TAB){
36378             if(e.shiftKey){
36379                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36380             }else{
36381                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36382                 forward = true;
36383             }
36384             
36385             e.stopEvent();
36386             
36387         } else if(k == e.ENTER &&  !e.ctrlKey){
36388             ed.completeEdit();
36389             e.stopEvent();
36390             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36391         
36392                 } else if(k == e.ESC){
36393             ed.cancelEdit();
36394         }
36395                 
36396         if (newCell) {
36397             var ecall = { cell : newCell, forward : forward };
36398             this.fireEvent('beforeeditnext', ecall );
36399             newCell = ecall.cell;
36400                         forward = ecall.forward;
36401         }
36402                 
36403         if(newCell){
36404             //Roo.log('next cell after edit');
36405             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36406         } else if (forward) {
36407             // tabbed past last
36408             this.fireEvent.defer(100, this, ['tabend',this]);
36409         }
36410     }
36411 });/*
36412  * Based on:
36413  * Ext JS Library 1.1.1
36414  * Copyright(c) 2006-2007, Ext JS, LLC.
36415  *
36416  * Originally Released Under LGPL - original licence link has changed is not relivant.
36417  *
36418  * Fork - LGPL
36419  * <script type="text/javascript">
36420  */
36421  
36422 /**
36423  * @class Roo.grid.EditorGrid
36424  * @extends Roo.grid.Grid
36425  * Class for creating and editable grid.
36426  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36427  * The container MUST have some type of size defined for the grid to fill. The container will be 
36428  * automatically set to position relative if it isn't already.
36429  * @param {Object} dataSource The data model to bind to
36430  * @param {Object} colModel The column model with info about this grid's columns
36431  */
36432 Roo.grid.EditorGrid = function(container, config){
36433     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36434     this.getGridEl().addClass("xedit-grid");
36435
36436     if(!this.selModel){
36437         this.selModel = new Roo.grid.CellSelectionModel();
36438     }
36439
36440     this.activeEditor = null;
36441
36442         this.addEvents({
36443             /**
36444              * @event beforeedit
36445              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36446              * <ul style="padding:5px;padding-left:16px;">
36447              * <li>grid - This grid</li>
36448              * <li>record - The record being edited</li>
36449              * <li>field - The field name being edited</li>
36450              * <li>value - The value for the field being edited.</li>
36451              * <li>row - The grid row index</li>
36452              * <li>column - The grid column index</li>
36453              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36454              * </ul>
36455              * @param {Object} e An edit event (see above for description)
36456              */
36457             "beforeedit" : true,
36458             /**
36459              * @event afteredit
36460              * Fires after a cell is edited. <br />
36461              * <ul style="padding:5px;padding-left:16px;">
36462              * <li>grid - This grid</li>
36463              * <li>record - The record being edited</li>
36464              * <li>field - The field name being edited</li>
36465              * <li>value - The value being set</li>
36466              * <li>originalValue - The original value for the field, before the edit.</li>
36467              * <li>row - The grid row index</li>
36468              * <li>column - The grid column index</li>
36469              * </ul>
36470              * @param {Object} e An edit event (see above for description)
36471              */
36472             "afteredit" : true,
36473             /**
36474              * @event validateedit
36475              * Fires after a cell is edited, but before the value is set in the record. 
36476          * You can use this to modify the value being set in the field, Return false
36477              * to cancel the change. The edit event object has the following properties <br />
36478              * <ul style="padding:5px;padding-left:16px;">
36479          * <li>editor - This editor</li>
36480              * <li>grid - This grid</li>
36481              * <li>record - The record being edited</li>
36482              * <li>field - The field name being edited</li>
36483              * <li>value - The value being set</li>
36484              * <li>originalValue - The original value for the field, before the edit.</li>
36485              * <li>row - The grid row index</li>
36486              * <li>column - The grid column index</li>
36487              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36488              * </ul>
36489              * @param {Object} e An edit event (see above for description)
36490              */
36491             "validateedit" : true
36492         });
36493     this.on("bodyscroll", this.stopEditing,  this);
36494     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36495 };
36496
36497 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36498     /**
36499      * @cfg {Number} clicksToEdit
36500      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36501      */
36502     clicksToEdit: 2,
36503
36504     // private
36505     isEditor : true,
36506     // private
36507     trackMouseOver: false, // causes very odd FF errors
36508
36509     onCellDblClick : function(g, row, col){
36510         this.startEditing(row, col);
36511     },
36512
36513     onEditComplete : function(ed, value, startValue){
36514         this.editing = false;
36515         this.activeEditor = null;
36516         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36517         var r = ed.record;
36518         var field = this.colModel.getDataIndex(ed.col);
36519         var e = {
36520             grid: this,
36521             record: r,
36522             field: field,
36523             originalValue: startValue,
36524             value: value,
36525             row: ed.row,
36526             column: ed.col,
36527             cancel:false,
36528             editor: ed
36529         };
36530         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36531         cell.show();
36532           
36533         if(String(value) !== String(startValue)){
36534             
36535             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36536                 r.set(field, e.value);
36537                 // if we are dealing with a combo box..
36538                 // then we also set the 'name' colum to be the displayField
36539                 if (ed.field.displayField && ed.field.name) {
36540                     r.set(ed.field.name, ed.field.el.dom.value);
36541                 }
36542                 
36543                 delete e.cancel; //?? why!!!
36544                 this.fireEvent("afteredit", e);
36545             }
36546         } else {
36547             this.fireEvent("afteredit", e); // always fire it!
36548         }
36549         this.view.focusCell(ed.row, ed.col);
36550     },
36551
36552     /**
36553      * Starts editing the specified for the specified row/column
36554      * @param {Number} rowIndex
36555      * @param {Number} colIndex
36556      */
36557     startEditing : function(row, col){
36558         this.stopEditing();
36559         if(this.colModel.isCellEditable(col, row)){
36560             this.view.ensureVisible(row, col, true);
36561           
36562             var r = this.dataSource.getAt(row);
36563             var field = this.colModel.getDataIndex(col);
36564             var cell = Roo.get(this.view.getCell(row,col));
36565             var e = {
36566                 grid: this,
36567                 record: r,
36568                 field: field,
36569                 value: r.data[field],
36570                 row: row,
36571                 column: col,
36572                 cancel:false 
36573             };
36574             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36575                 this.editing = true;
36576                 var ed = this.colModel.getCellEditor(col, row);
36577                 
36578                 if (!ed) {
36579                     return;
36580                 }
36581                 if(!ed.rendered){
36582                     ed.render(ed.parentEl || document.body);
36583                 }
36584                 ed.field.reset();
36585                
36586                 cell.hide();
36587                 
36588                 (function(){ // complex but required for focus issues in safari, ie and opera
36589                     ed.row = row;
36590                     ed.col = col;
36591                     ed.record = r;
36592                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36593                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36594                     this.activeEditor = ed;
36595                     var v = r.data[field];
36596                     ed.startEdit(this.view.getCell(row, col), v);
36597                     // combo's with 'displayField and name set
36598                     if (ed.field.displayField && ed.field.name) {
36599                         ed.field.el.dom.value = r.data[ed.field.name];
36600                     }
36601                     
36602                     
36603                 }).defer(50, this);
36604             }
36605         }
36606     },
36607         
36608     /**
36609      * Stops any active editing
36610      */
36611     stopEditing : function(){
36612         if(this.activeEditor){
36613             this.activeEditor.completeEdit();
36614         }
36615         this.activeEditor = null;
36616     },
36617         
36618          /**
36619      * Called to get grid's drag proxy text, by default returns this.ddText.
36620      * @return {String}
36621      */
36622     getDragDropText : function(){
36623         var count = this.selModel.getSelectedCell() ? 1 : 0;
36624         return String.format(this.ddText, count, count == 1 ? '' : 's');
36625     }
36626         
36627 });/*
36628  * Based on:
36629  * Ext JS Library 1.1.1
36630  * Copyright(c) 2006-2007, Ext JS, LLC.
36631  *
36632  * Originally Released Under LGPL - original licence link has changed is not relivant.
36633  *
36634  * Fork - LGPL
36635  * <script type="text/javascript">
36636  */
36637
36638 // private - not really -- you end up using it !
36639 // This is a support class used internally by the Grid components
36640
36641 /**
36642  * @class Roo.grid.GridEditor
36643  * @extends Roo.Editor
36644  * Class for creating and editable grid elements.
36645  * @param {Object} config any settings (must include field)
36646  */
36647 Roo.grid.GridEditor = function(field, config){
36648     if (!config && field.field) {
36649         config = field;
36650         field = Roo.factory(config.field, Roo.form);
36651     }
36652     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36653     field.monitorTab = false;
36654 };
36655
36656 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36657     
36658     /**
36659      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36660      */
36661     
36662     alignment: "tl-tl",
36663     autoSize: "width",
36664     hideEl : false,
36665     cls: "x-small-editor x-grid-editor",
36666     shim:false,
36667     shadow:"frame"
36668 });/*
36669  * Based on:
36670  * Ext JS Library 1.1.1
36671  * Copyright(c) 2006-2007, Ext JS, LLC.
36672  *
36673  * Originally Released Under LGPL - original licence link has changed is not relivant.
36674  *
36675  * Fork - LGPL
36676  * <script type="text/javascript">
36677  */
36678   
36679
36680   
36681 Roo.grid.PropertyRecord = Roo.data.Record.create([
36682     {name:'name',type:'string'},  'value'
36683 ]);
36684
36685
36686 Roo.grid.PropertyStore = function(grid, source){
36687     this.grid = grid;
36688     this.store = new Roo.data.Store({
36689         recordType : Roo.grid.PropertyRecord
36690     });
36691     this.store.on('update', this.onUpdate,  this);
36692     if(source){
36693         this.setSource(source);
36694     }
36695     Roo.grid.PropertyStore.superclass.constructor.call(this);
36696 };
36697
36698
36699
36700 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36701     setSource : function(o){
36702         this.source = o;
36703         this.store.removeAll();
36704         var data = [];
36705         for(var k in o){
36706             if(this.isEditableValue(o[k])){
36707                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36708             }
36709         }
36710         this.store.loadRecords({records: data}, {}, true);
36711     },
36712
36713     onUpdate : function(ds, record, type){
36714         if(type == Roo.data.Record.EDIT){
36715             var v = record.data['value'];
36716             var oldValue = record.modified['value'];
36717             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36718                 this.source[record.id] = v;
36719                 record.commit();
36720                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36721             }else{
36722                 record.reject();
36723             }
36724         }
36725     },
36726
36727     getProperty : function(row){
36728        return this.store.getAt(row);
36729     },
36730
36731     isEditableValue: function(val){
36732         if(val && val instanceof Date){
36733             return true;
36734         }else if(typeof val == 'object' || typeof val == 'function'){
36735             return false;
36736         }
36737         return true;
36738     },
36739
36740     setValue : function(prop, value){
36741         this.source[prop] = value;
36742         this.store.getById(prop).set('value', value);
36743     },
36744
36745     getSource : function(){
36746         return this.source;
36747     }
36748 });
36749
36750 Roo.grid.PropertyColumnModel = function(grid, store){
36751     this.grid = grid;
36752     var g = Roo.grid;
36753     g.PropertyColumnModel.superclass.constructor.call(this, [
36754         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36755         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36756     ]);
36757     this.store = store;
36758     this.bselect = Roo.DomHelper.append(document.body, {
36759         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36760             {tag: 'option', value: 'true', html: 'true'},
36761             {tag: 'option', value: 'false', html: 'false'}
36762         ]
36763     });
36764     Roo.id(this.bselect);
36765     var f = Roo.form;
36766     this.editors = {
36767         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36768         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36769         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36770         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36771         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36772     };
36773     this.renderCellDelegate = this.renderCell.createDelegate(this);
36774     this.renderPropDelegate = this.renderProp.createDelegate(this);
36775 };
36776
36777 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36778     
36779     
36780     nameText : 'Name',
36781     valueText : 'Value',
36782     
36783     dateFormat : 'm/j/Y',
36784     
36785     
36786     renderDate : function(dateVal){
36787         return dateVal.dateFormat(this.dateFormat);
36788     },
36789
36790     renderBool : function(bVal){
36791         return bVal ? 'true' : 'false';
36792     },
36793
36794     isCellEditable : function(colIndex, rowIndex){
36795         return colIndex == 1;
36796     },
36797
36798     getRenderer : function(col){
36799         return col == 1 ?
36800             this.renderCellDelegate : this.renderPropDelegate;
36801     },
36802
36803     renderProp : function(v){
36804         return this.getPropertyName(v);
36805     },
36806
36807     renderCell : function(val){
36808         var rv = val;
36809         if(val instanceof Date){
36810             rv = this.renderDate(val);
36811         }else if(typeof val == 'boolean'){
36812             rv = this.renderBool(val);
36813         }
36814         return Roo.util.Format.htmlEncode(rv);
36815     },
36816
36817     getPropertyName : function(name){
36818         var pn = this.grid.propertyNames;
36819         return pn && pn[name] ? pn[name] : name;
36820     },
36821
36822     getCellEditor : function(colIndex, rowIndex){
36823         var p = this.store.getProperty(rowIndex);
36824         var n = p.data['name'], val = p.data['value'];
36825         
36826         if(typeof(this.grid.customEditors[n]) == 'string'){
36827             return this.editors[this.grid.customEditors[n]];
36828         }
36829         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36830             return this.grid.customEditors[n];
36831         }
36832         if(val instanceof Date){
36833             return this.editors['date'];
36834         }else if(typeof val == 'number'){
36835             return this.editors['number'];
36836         }else if(typeof val == 'boolean'){
36837             return this.editors['boolean'];
36838         }else{
36839             return this.editors['string'];
36840         }
36841     }
36842 });
36843
36844 /**
36845  * @class Roo.grid.PropertyGrid
36846  * @extends Roo.grid.EditorGrid
36847  * This class represents the  interface of a component based property grid control.
36848  * <br><br>Usage:<pre><code>
36849  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36850       
36851  });
36852  // set any options
36853  grid.render();
36854  * </code></pre>
36855   
36856  * @constructor
36857  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36858  * The container MUST have some type of size defined for the grid to fill. The container will be
36859  * automatically set to position relative if it isn't already.
36860  * @param {Object} config A config object that sets properties on this grid.
36861  */
36862 Roo.grid.PropertyGrid = function(container, config){
36863     config = config || {};
36864     var store = new Roo.grid.PropertyStore(this);
36865     this.store = store;
36866     var cm = new Roo.grid.PropertyColumnModel(this, store);
36867     store.store.sort('name', 'ASC');
36868     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36869         ds: store.store,
36870         cm: cm,
36871         enableColLock:false,
36872         enableColumnMove:false,
36873         stripeRows:false,
36874         trackMouseOver: false,
36875         clicksToEdit:1
36876     }, config));
36877     this.getGridEl().addClass('x-props-grid');
36878     this.lastEditRow = null;
36879     this.on('columnresize', this.onColumnResize, this);
36880     this.addEvents({
36881          /**
36882              * @event beforepropertychange
36883              * Fires before a property changes (return false to stop?)
36884              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36885              * @param {String} id Record Id
36886              * @param {String} newval New Value
36887          * @param {String} oldval Old Value
36888              */
36889         "beforepropertychange": true,
36890         /**
36891              * @event propertychange
36892              * Fires after a property changes
36893              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36894              * @param {String} id Record Id
36895              * @param {String} newval New Value
36896          * @param {String} oldval Old Value
36897              */
36898         "propertychange": true
36899     });
36900     this.customEditors = this.customEditors || {};
36901 };
36902 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36903     
36904      /**
36905      * @cfg {Object} customEditors map of colnames=> custom editors.
36906      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36907      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36908      * false disables editing of the field.
36909          */
36910     
36911       /**
36912      * @cfg {Object} propertyNames map of property Names to their displayed value
36913          */
36914     
36915     render : function(){
36916         Roo.grid.PropertyGrid.superclass.render.call(this);
36917         this.autoSize.defer(100, this);
36918     },
36919
36920     autoSize : function(){
36921         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36922         if(this.view){
36923             this.view.fitColumns();
36924         }
36925     },
36926
36927     onColumnResize : function(){
36928         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36929         this.autoSize();
36930     },
36931     /**
36932      * Sets the data for the Grid
36933      * accepts a Key => Value object of all the elements avaiable.
36934      * @param {Object} data  to appear in grid.
36935      */
36936     setSource : function(source){
36937         this.store.setSource(source);
36938         //this.autoSize();
36939     },
36940     /**
36941      * Gets all the data from the grid.
36942      * @return {Object} data  data stored in grid
36943      */
36944     getSource : function(){
36945         return this.store.getSource();
36946     }
36947 });/*
36948   
36949  * Licence LGPL
36950  
36951  */
36952  
36953 /**
36954  * @class Roo.grid.Calendar
36955  * @extends Roo.util.Grid
36956  * This class extends the Grid to provide a calendar widget
36957  * <br><br>Usage:<pre><code>
36958  var grid = new Roo.grid.Calendar("my-container-id", {
36959      ds: myDataStore,
36960      cm: myColModel,
36961      selModel: mySelectionModel,
36962      autoSizeColumns: true,
36963      monitorWindowResize: false,
36964      trackMouseOver: true
36965      eventstore : real data store..
36966  });
36967  // set any options
36968  grid.render();
36969   
36970   * @constructor
36971  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36972  * The container MUST have some type of size defined for the grid to fill. The container will be
36973  * automatically set to position relative if it isn't already.
36974  * @param {Object} config A config object that sets properties on this grid.
36975  */
36976 Roo.grid.Calendar = function(container, config){
36977         // initialize the container
36978         this.container = Roo.get(container);
36979         this.container.update("");
36980         this.container.setStyle("overflow", "hidden");
36981     this.container.addClass('x-grid-container');
36982
36983     this.id = this.container.id;
36984
36985     Roo.apply(this, config);
36986     // check and correct shorthanded configs
36987     
36988     var rows = [];
36989     var d =1;
36990     for (var r = 0;r < 6;r++) {
36991         
36992         rows[r]=[];
36993         for (var c =0;c < 7;c++) {
36994             rows[r][c]= '';
36995         }
36996     }
36997     if (this.eventStore) {
36998         this.eventStore= Roo.factory(this.eventStore, Roo.data);
36999         this.eventStore.on('load',this.onLoad, this);
37000         this.eventStore.on('beforeload',this.clearEvents, this);
37001          
37002     }
37003     
37004     this.dataSource = new Roo.data.Store({
37005             proxy: new Roo.data.MemoryProxy(rows),
37006             reader: new Roo.data.ArrayReader({}, [
37007                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37008     });
37009
37010     this.dataSource.load();
37011     this.ds = this.dataSource;
37012     this.ds.xmodule = this.xmodule || false;
37013     
37014     
37015     var cellRender = function(v,x,r)
37016     {
37017         return String.format(
37018             '<div class="fc-day  fc-widget-content"><div>' +
37019                 '<div class="fc-event-container"></div>' +
37020                 '<div class="fc-day-number">{0}</div>'+
37021                 
37022                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37023             '</div></div>', v);
37024     
37025     }
37026     
37027     
37028     this.colModel = new Roo.grid.ColumnModel( [
37029         {
37030             xtype: 'ColumnModel',
37031             xns: Roo.grid,
37032             dataIndex : 'weekday0',
37033             header : 'Sunday',
37034             renderer : cellRender
37035         },
37036         {
37037             xtype: 'ColumnModel',
37038             xns: Roo.grid,
37039             dataIndex : 'weekday1',
37040             header : 'Monday',
37041             renderer : cellRender
37042         },
37043         {
37044             xtype: 'ColumnModel',
37045             xns: Roo.grid,
37046             dataIndex : 'weekday2',
37047             header : 'Tuesday',
37048             renderer : cellRender
37049         },
37050         {
37051             xtype: 'ColumnModel',
37052             xns: Roo.grid,
37053             dataIndex : 'weekday3',
37054             header : 'Wednesday',
37055             renderer : cellRender
37056         },
37057         {
37058             xtype: 'ColumnModel',
37059             xns: Roo.grid,
37060             dataIndex : 'weekday4',
37061             header : 'Thursday',
37062             renderer : cellRender
37063         },
37064         {
37065             xtype: 'ColumnModel',
37066             xns: Roo.grid,
37067             dataIndex : 'weekday5',
37068             header : 'Friday',
37069             renderer : cellRender
37070         },
37071         {
37072             xtype: 'ColumnModel',
37073             xns: Roo.grid,
37074             dataIndex : 'weekday6',
37075             header : 'Saturday',
37076             renderer : cellRender
37077         }
37078     ]);
37079     this.cm = this.colModel;
37080     this.cm.xmodule = this.xmodule || false;
37081  
37082         
37083           
37084     //this.selModel = new Roo.grid.CellSelectionModel();
37085     //this.sm = this.selModel;
37086     //this.selModel.init(this);
37087     
37088     
37089     if(this.width){
37090         this.container.setWidth(this.width);
37091     }
37092
37093     if(this.height){
37094         this.container.setHeight(this.height);
37095     }
37096     /** @private */
37097         this.addEvents({
37098         // raw events
37099         /**
37100          * @event click
37101          * The raw click event for the entire grid.
37102          * @param {Roo.EventObject} e
37103          */
37104         "click" : true,
37105         /**
37106          * @event dblclick
37107          * The raw dblclick event for the entire grid.
37108          * @param {Roo.EventObject} e
37109          */
37110         "dblclick" : true,
37111         /**
37112          * @event contextmenu
37113          * The raw contextmenu event for the entire grid.
37114          * @param {Roo.EventObject} e
37115          */
37116         "contextmenu" : true,
37117         /**
37118          * @event mousedown
37119          * The raw mousedown event for the entire grid.
37120          * @param {Roo.EventObject} e
37121          */
37122         "mousedown" : true,
37123         /**
37124          * @event mouseup
37125          * The raw mouseup event for the entire grid.
37126          * @param {Roo.EventObject} e
37127          */
37128         "mouseup" : true,
37129         /**
37130          * @event mouseover
37131          * The raw mouseover event for the entire grid.
37132          * @param {Roo.EventObject} e
37133          */
37134         "mouseover" : true,
37135         /**
37136          * @event mouseout
37137          * The raw mouseout event for the entire grid.
37138          * @param {Roo.EventObject} e
37139          */
37140         "mouseout" : true,
37141         /**
37142          * @event keypress
37143          * The raw keypress event for the entire grid.
37144          * @param {Roo.EventObject} e
37145          */
37146         "keypress" : true,
37147         /**
37148          * @event keydown
37149          * The raw keydown event for the entire grid.
37150          * @param {Roo.EventObject} e
37151          */
37152         "keydown" : true,
37153
37154         // custom events
37155
37156         /**
37157          * @event cellclick
37158          * Fires when a cell is clicked
37159          * @param {Grid} this
37160          * @param {Number} rowIndex
37161          * @param {Number} columnIndex
37162          * @param {Roo.EventObject} e
37163          */
37164         "cellclick" : true,
37165         /**
37166          * @event celldblclick
37167          * Fires when a cell is double clicked
37168          * @param {Grid} this
37169          * @param {Number} rowIndex
37170          * @param {Number} columnIndex
37171          * @param {Roo.EventObject} e
37172          */
37173         "celldblclick" : true,
37174         /**
37175          * @event rowclick
37176          * Fires when a row is clicked
37177          * @param {Grid} this
37178          * @param {Number} rowIndex
37179          * @param {Roo.EventObject} e
37180          */
37181         "rowclick" : true,
37182         /**
37183          * @event rowdblclick
37184          * Fires when a row is double clicked
37185          * @param {Grid} this
37186          * @param {Number} rowIndex
37187          * @param {Roo.EventObject} e
37188          */
37189         "rowdblclick" : true,
37190         /**
37191          * @event headerclick
37192          * Fires when a header is clicked
37193          * @param {Grid} this
37194          * @param {Number} columnIndex
37195          * @param {Roo.EventObject} e
37196          */
37197         "headerclick" : true,
37198         /**
37199          * @event headerdblclick
37200          * Fires when a header cell is double clicked
37201          * @param {Grid} this
37202          * @param {Number} columnIndex
37203          * @param {Roo.EventObject} e
37204          */
37205         "headerdblclick" : true,
37206         /**
37207          * @event rowcontextmenu
37208          * Fires when a row is right clicked
37209          * @param {Grid} this
37210          * @param {Number} rowIndex
37211          * @param {Roo.EventObject} e
37212          */
37213         "rowcontextmenu" : true,
37214         /**
37215          * @event cellcontextmenu
37216          * Fires when a cell is right clicked
37217          * @param {Grid} this
37218          * @param {Number} rowIndex
37219          * @param {Number} cellIndex
37220          * @param {Roo.EventObject} e
37221          */
37222          "cellcontextmenu" : true,
37223         /**
37224          * @event headercontextmenu
37225          * Fires when a header is right clicked
37226          * @param {Grid} this
37227          * @param {Number} columnIndex
37228          * @param {Roo.EventObject} e
37229          */
37230         "headercontextmenu" : true,
37231         /**
37232          * @event bodyscroll
37233          * Fires when the body element is scrolled
37234          * @param {Number} scrollLeft
37235          * @param {Number} scrollTop
37236          */
37237         "bodyscroll" : true,
37238         /**
37239          * @event columnresize
37240          * Fires when the user resizes a column
37241          * @param {Number} columnIndex
37242          * @param {Number} newSize
37243          */
37244         "columnresize" : true,
37245         /**
37246          * @event columnmove
37247          * Fires when the user moves a column
37248          * @param {Number} oldIndex
37249          * @param {Number} newIndex
37250          */
37251         "columnmove" : true,
37252         /**
37253          * @event startdrag
37254          * Fires when row(s) start being dragged
37255          * @param {Grid} this
37256          * @param {Roo.GridDD} dd The drag drop object
37257          * @param {event} e The raw browser event
37258          */
37259         "startdrag" : true,
37260         /**
37261          * @event enddrag
37262          * Fires when a drag operation is complete
37263          * @param {Grid} this
37264          * @param {Roo.GridDD} dd The drag drop object
37265          * @param {event} e The raw browser event
37266          */
37267         "enddrag" : true,
37268         /**
37269          * @event dragdrop
37270          * Fires when dragged row(s) are dropped on a valid DD target
37271          * @param {Grid} this
37272          * @param {Roo.GridDD} dd The drag drop object
37273          * @param {String} targetId The target drag drop object
37274          * @param {event} e The raw browser event
37275          */
37276         "dragdrop" : true,
37277         /**
37278          * @event dragover
37279          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37280          * @param {Grid} this
37281          * @param {Roo.GridDD} dd The drag drop object
37282          * @param {String} targetId The target drag drop object
37283          * @param {event} e The raw browser event
37284          */
37285         "dragover" : true,
37286         /**
37287          * @event dragenter
37288          *  Fires when the dragged row(s) first cross another DD target while being dragged
37289          * @param {Grid} this
37290          * @param {Roo.GridDD} dd The drag drop object
37291          * @param {String} targetId The target drag drop object
37292          * @param {event} e The raw browser event
37293          */
37294         "dragenter" : true,
37295         /**
37296          * @event dragout
37297          * Fires when the dragged row(s) leave another DD target while being dragged
37298          * @param {Grid} this
37299          * @param {Roo.GridDD} dd The drag drop object
37300          * @param {String} targetId The target drag drop object
37301          * @param {event} e The raw browser event
37302          */
37303         "dragout" : true,
37304         /**
37305          * @event rowclass
37306          * Fires when a row is rendered, so you can change add a style to it.
37307          * @param {GridView} gridview   The grid view
37308          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37309          */
37310         'rowclass' : true,
37311
37312         /**
37313          * @event render
37314          * Fires when the grid is rendered
37315          * @param {Grid} grid
37316          */
37317         'render' : true,
37318             /**
37319              * @event select
37320              * Fires when a date is selected
37321              * @param {DatePicker} this
37322              * @param {Date} date The selected date
37323              */
37324         'select': true,
37325         /**
37326              * @event monthchange
37327              * Fires when the displayed month changes 
37328              * @param {DatePicker} this
37329              * @param {Date} date The selected month
37330              */
37331         'monthchange': true,
37332         /**
37333              * @event evententer
37334              * Fires when mouse over an event
37335              * @param {Calendar} this
37336              * @param {event} Event
37337              */
37338         'evententer': true,
37339         /**
37340              * @event eventleave
37341              * Fires when the mouse leaves an
37342              * @param {Calendar} this
37343              * @param {event}
37344              */
37345         'eventleave': true,
37346         /**
37347              * @event eventclick
37348              * Fires when the mouse click an
37349              * @param {Calendar} this
37350              * @param {event}
37351              */
37352         'eventclick': true,
37353         /**
37354              * @event eventrender
37355              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37356              * @param {Calendar} this
37357              * @param {data} data to be modified
37358              */
37359         'eventrender': true
37360         
37361     });
37362
37363     Roo.grid.Grid.superclass.constructor.call(this);
37364     this.on('render', function() {
37365         this.view.el.addClass('x-grid-cal'); 
37366         
37367         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37368
37369     },this);
37370     
37371     if (!Roo.grid.Calendar.style) {
37372         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37373             
37374             
37375             '.x-grid-cal .x-grid-col' :  {
37376                 height: 'auto !important',
37377                 'vertical-align': 'top'
37378             },
37379             '.x-grid-cal  .fc-event-hori' : {
37380                 height: '14px'
37381             }
37382              
37383             
37384         }, Roo.id());
37385     }
37386
37387     
37388     
37389 };
37390 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37391     /**
37392      * @cfg {Store} eventStore The store that loads events.
37393      */
37394     eventStore : 25,
37395
37396      
37397     activeDate : false,
37398     startDay : 0,
37399     autoWidth : true,
37400     monitorWindowResize : false,
37401
37402     
37403     resizeColumns : function() {
37404         var col = (this.view.el.getWidth() / 7) - 3;
37405         // loop through cols, and setWidth
37406         for(var i =0 ; i < 7 ; i++){
37407             this.cm.setColumnWidth(i, col);
37408         }
37409     },
37410      setDate :function(date) {
37411         
37412         Roo.log('setDate?');
37413         
37414         this.resizeColumns();
37415         var vd = this.activeDate;
37416         this.activeDate = date;
37417 //        if(vd && this.el){
37418 //            var t = date.getTime();
37419 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37420 //                Roo.log('using add remove');
37421 //                
37422 //                this.fireEvent('monthchange', this, date);
37423 //                
37424 //                this.cells.removeClass("fc-state-highlight");
37425 //                this.cells.each(function(c){
37426 //                   if(c.dateValue == t){
37427 //                       c.addClass("fc-state-highlight");
37428 //                       setTimeout(function(){
37429 //                            try{c.dom.firstChild.focus();}catch(e){}
37430 //                       }, 50);
37431 //                       return false;
37432 //                   }
37433 //                   return true;
37434 //                });
37435 //                return;
37436 //            }
37437 //        }
37438         
37439         var days = date.getDaysInMonth();
37440         
37441         var firstOfMonth = date.getFirstDateOfMonth();
37442         var startingPos = firstOfMonth.getDay()-this.startDay;
37443         
37444         if(startingPos < this.startDay){
37445             startingPos += 7;
37446         }
37447         
37448         var pm = date.add(Date.MONTH, -1);
37449         var prevStart = pm.getDaysInMonth()-startingPos;
37450 //        
37451         
37452         
37453         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37454         
37455         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37456         //this.cells.addClassOnOver('fc-state-hover');
37457         
37458         var cells = this.cells.elements;
37459         var textEls = this.textNodes;
37460         
37461         //Roo.each(cells, function(cell){
37462         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37463         //});
37464         
37465         days += startingPos;
37466
37467         // convert everything to numbers so it's fast
37468         var day = 86400000;
37469         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37470         //Roo.log(d);
37471         //Roo.log(pm);
37472         //Roo.log(prevStart);
37473         
37474         var today = new Date().clearTime().getTime();
37475         var sel = date.clearTime().getTime();
37476         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37477         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37478         var ddMatch = this.disabledDatesRE;
37479         var ddText = this.disabledDatesText;
37480         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37481         var ddaysText = this.disabledDaysText;
37482         var format = this.format;
37483         
37484         var setCellClass = function(cal, cell){
37485             
37486             //Roo.log('set Cell Class');
37487             cell.title = "";
37488             var t = d.getTime();
37489             
37490             //Roo.log(d);
37491             
37492             
37493             cell.dateValue = t;
37494             if(t == today){
37495                 cell.className += " fc-today";
37496                 cell.className += " fc-state-highlight";
37497                 cell.title = cal.todayText;
37498             }
37499             if(t == sel){
37500                 // disable highlight in other month..
37501                 cell.className += " fc-state-highlight";
37502                 
37503             }
37504             // disabling
37505             if(t < min) {
37506                 //cell.className = " fc-state-disabled";
37507                 cell.title = cal.minText;
37508                 return;
37509             }
37510             if(t > max) {
37511                 //cell.className = " fc-state-disabled";
37512                 cell.title = cal.maxText;
37513                 return;
37514             }
37515             if(ddays){
37516                 if(ddays.indexOf(d.getDay()) != -1){
37517                     // cell.title = ddaysText;
37518                    // cell.className = " fc-state-disabled";
37519                 }
37520             }
37521             if(ddMatch && format){
37522                 var fvalue = d.dateFormat(format);
37523                 if(ddMatch.test(fvalue)){
37524                     cell.title = ddText.replace("%0", fvalue);
37525                    cell.className = " fc-state-disabled";
37526                 }
37527             }
37528             
37529             if (!cell.initialClassName) {
37530                 cell.initialClassName = cell.dom.className;
37531             }
37532             
37533             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37534         };
37535
37536         var i = 0;
37537         
37538         for(; i < startingPos; i++) {
37539             cells[i].dayName =  (++prevStart);
37540             Roo.log(textEls[i]);
37541             d.setDate(d.getDate()+1);
37542             
37543             //cells[i].className = "fc-past fc-other-month";
37544             setCellClass(this, cells[i]);
37545         }
37546         
37547         var intDay = 0;
37548         
37549         for(; i < days; i++){
37550             intDay = i - startingPos + 1;
37551             cells[i].dayName =  (intDay);
37552             d.setDate(d.getDate()+1);
37553             
37554             cells[i].className = ''; // "x-date-active";
37555             setCellClass(this, cells[i]);
37556         }
37557         var extraDays = 0;
37558         
37559         for(; i < 42; i++) {
37560             //textEls[i].innerHTML = (++extraDays);
37561             
37562             d.setDate(d.getDate()+1);
37563             cells[i].dayName = (++extraDays);
37564             cells[i].className = "fc-future fc-other-month";
37565             setCellClass(this, cells[i]);
37566         }
37567         
37568         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37569         
37570         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37571         
37572         // this will cause all the cells to mis
37573         var rows= [];
37574         var i =0;
37575         for (var r = 0;r < 6;r++) {
37576             for (var c =0;c < 7;c++) {
37577                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37578             }    
37579         }
37580         
37581         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37582         for(i=0;i<cells.length;i++) {
37583             
37584             this.cells.elements[i].dayName = cells[i].dayName ;
37585             this.cells.elements[i].className = cells[i].className;
37586             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37587             this.cells.elements[i].title = cells[i].title ;
37588             this.cells.elements[i].dateValue = cells[i].dateValue ;
37589         }
37590         
37591         
37592         
37593         
37594         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37595         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37596         
37597         ////if(totalRows != 6){
37598             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37599            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37600        // }
37601         
37602         this.fireEvent('monthchange', this, date);
37603         
37604         
37605     },
37606  /**
37607      * Returns the grid's SelectionModel.
37608      * @return {SelectionModel}
37609      */
37610     getSelectionModel : function(){
37611         if(!this.selModel){
37612             this.selModel = new Roo.grid.CellSelectionModel();
37613         }
37614         return this.selModel;
37615     },
37616
37617     load: function() {
37618         this.eventStore.load()
37619         
37620         
37621         
37622     },
37623     
37624     findCell : function(dt) {
37625         dt = dt.clearTime().getTime();
37626         var ret = false;
37627         this.cells.each(function(c){
37628             //Roo.log("check " +c.dateValue + '?=' + dt);
37629             if(c.dateValue == dt){
37630                 ret = c;
37631                 return false;
37632             }
37633             return true;
37634         });
37635         
37636         return ret;
37637     },
37638     
37639     findCells : function(rec) {
37640         var s = rec.data.start_dt.clone().clearTime().getTime();
37641        // Roo.log(s);
37642         var e= rec.data.end_dt.clone().clearTime().getTime();
37643        // Roo.log(e);
37644         var ret = [];
37645         this.cells.each(function(c){
37646              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37647             
37648             if(c.dateValue > e){
37649                 return ;
37650             }
37651             if(c.dateValue < s){
37652                 return ;
37653             }
37654             ret.push(c);
37655         });
37656         
37657         return ret;    
37658     },
37659     
37660     findBestRow: function(cells)
37661     {
37662         var ret = 0;
37663         
37664         for (var i =0 ; i < cells.length;i++) {
37665             ret  = Math.max(cells[i].rows || 0,ret);
37666         }
37667         return ret;
37668         
37669     },
37670     
37671     
37672     addItem : function(rec)
37673     {
37674         // look for vertical location slot in
37675         var cells = this.findCells(rec);
37676         
37677         rec.row = this.findBestRow(cells);
37678         
37679         // work out the location.
37680         
37681         var crow = false;
37682         var rows = [];
37683         for(var i =0; i < cells.length; i++) {
37684             if (!crow) {
37685                 crow = {
37686                     start : cells[i],
37687                     end :  cells[i]
37688                 };
37689                 continue;
37690             }
37691             if (crow.start.getY() == cells[i].getY()) {
37692                 // on same row.
37693                 crow.end = cells[i];
37694                 continue;
37695             }
37696             // different row.
37697             rows.push(crow);
37698             crow = {
37699                 start: cells[i],
37700                 end : cells[i]
37701             };
37702             
37703         }
37704         
37705         rows.push(crow);
37706         rec.els = [];
37707         rec.rows = rows;
37708         rec.cells = cells;
37709         for (var i = 0; i < cells.length;i++) {
37710             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37711             
37712         }
37713         
37714         
37715     },
37716     
37717     clearEvents: function() {
37718         
37719         if (!this.eventStore.getCount()) {
37720             return;
37721         }
37722         // reset number of rows in cells.
37723         Roo.each(this.cells.elements, function(c){
37724             c.rows = 0;
37725         });
37726         
37727         this.eventStore.each(function(e) {
37728             this.clearEvent(e);
37729         },this);
37730         
37731     },
37732     
37733     clearEvent : function(ev)
37734     {
37735         if (ev.els) {
37736             Roo.each(ev.els, function(el) {
37737                 el.un('mouseenter' ,this.onEventEnter, this);
37738                 el.un('mouseleave' ,this.onEventLeave, this);
37739                 el.remove();
37740             },this);
37741             ev.els = [];
37742         }
37743     },
37744     
37745     
37746     renderEvent : function(ev,ctr) {
37747         if (!ctr) {
37748              ctr = this.view.el.select('.fc-event-container',true).first();
37749         }
37750         
37751          
37752         this.clearEvent(ev);
37753             //code
37754        
37755         
37756         
37757         ev.els = [];
37758         var cells = ev.cells;
37759         var rows = ev.rows;
37760         this.fireEvent('eventrender', this, ev);
37761         
37762         for(var i =0; i < rows.length; i++) {
37763             
37764             cls = '';
37765             if (i == 0) {
37766                 cls += ' fc-event-start';
37767             }
37768             if ((i+1) == rows.length) {
37769                 cls += ' fc-event-end';
37770             }
37771             
37772             //Roo.log(ev.data);
37773             // how many rows should it span..
37774             var cg = this.eventTmpl.append(ctr,Roo.apply({
37775                 fccls : cls
37776                 
37777             }, ev.data) , true);
37778             
37779             
37780             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37781             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37782             cg.on('click', this.onEventClick, this, ev);
37783             
37784             ev.els.push(cg);
37785             
37786             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37787             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37788             //Roo.log(cg);
37789              
37790             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
37791             cg.setWidth(ebox.right - sbox.x -2);
37792         }
37793     },
37794     
37795     renderEvents: function()
37796     {   
37797         // first make sure there is enough space..
37798         
37799         if (!this.eventTmpl) {
37800             this.eventTmpl = new Roo.Template(
37801                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
37802                     '<div class="fc-event-inner">' +
37803                         '<span class="fc-event-time">{time}</span>' +
37804                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37805                     '</div>' +
37806                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
37807                 '</div>'
37808             );
37809                 
37810         }
37811                
37812         
37813         
37814         this.cells.each(function(c) {
37815             //Roo.log(c.select('.fc-day-content div',true).first());
37816             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37817         });
37818         
37819         var ctr = this.view.el.select('.fc-event-container',true).first();
37820         
37821         var cls;
37822         this.eventStore.each(function(ev){
37823             
37824             this.renderEvent(ev);
37825              
37826              
37827         }, this);
37828         this.view.layout();
37829         
37830     },
37831     
37832     onEventEnter: function (e, el,event,d) {
37833         this.fireEvent('evententer', this, el, event);
37834     },
37835     
37836     onEventLeave: function (e, el,event,d) {
37837         this.fireEvent('eventleave', this, el, event);
37838     },
37839     
37840     onEventClick: function (e, el,event,d) {
37841         this.fireEvent('eventclick', this, el, event);
37842     },
37843     
37844     onMonthChange: function () {
37845         this.store.load();
37846     },
37847     
37848     onLoad: function () {
37849         
37850         //Roo.log('calendar onload');
37851 //         
37852         if(this.eventStore.getCount() > 0){
37853             
37854            
37855             
37856             this.eventStore.each(function(d){
37857                 
37858                 
37859                 // FIXME..
37860                 var add =   d.data;
37861                 if (typeof(add.end_dt) == 'undefined')  {
37862                     Roo.log("Missing End time in calendar data: ");
37863                     Roo.log(d);
37864                     return;
37865                 }
37866                 if (typeof(add.start_dt) == 'undefined')  {
37867                     Roo.log("Missing Start time in calendar data: ");
37868                     Roo.log(d);
37869                     return;
37870                 }
37871                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
37872                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
37873                 add.id = add.id || d.id;
37874                 add.title = add.title || '??';
37875                 
37876                 this.addItem(d);
37877                 
37878              
37879             },this);
37880         }
37881         
37882         this.renderEvents();
37883     }
37884     
37885
37886 });
37887 /*
37888  grid : {
37889                 xtype: 'Grid',
37890                 xns: Roo.grid,
37891                 listeners : {
37892                     render : function ()
37893                     {
37894                         _this.grid = this;
37895                         
37896                         if (!this.view.el.hasClass('course-timesheet')) {
37897                             this.view.el.addClass('course-timesheet');
37898                         }
37899                         if (this.tsStyle) {
37900                             this.ds.load({});
37901                             return; 
37902                         }
37903                         Roo.log('width');
37904                         Roo.log(_this.grid.view.el.getWidth());
37905                         
37906                         
37907                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
37908                             '.course-timesheet .x-grid-row' : {
37909                                 height: '80px'
37910                             },
37911                             '.x-grid-row td' : {
37912                                 'vertical-align' : 0
37913                             },
37914                             '.course-edit-link' : {
37915                                 'color' : 'blue',
37916                                 'text-overflow' : 'ellipsis',
37917                                 'overflow' : 'hidden',
37918                                 'white-space' : 'nowrap',
37919                                 'cursor' : 'pointer'
37920                             },
37921                             '.sub-link' : {
37922                                 'color' : 'green'
37923                             },
37924                             '.de-act-sup-link' : {
37925                                 'color' : 'purple',
37926                                 'text-decoration' : 'line-through'
37927                             },
37928                             '.de-act-link' : {
37929                                 'color' : 'red',
37930                                 'text-decoration' : 'line-through'
37931                             },
37932                             '.course-timesheet .course-highlight' : {
37933                                 'border-top-style': 'dashed !important',
37934                                 'border-bottom-bottom': 'dashed !important'
37935                             },
37936                             '.course-timesheet .course-item' : {
37937                                 'font-family'   : 'tahoma, arial, helvetica',
37938                                 'font-size'     : '11px',
37939                                 'overflow'      : 'hidden',
37940                                 'padding-left'  : '10px',
37941                                 'padding-right' : '10px',
37942                                 'padding-top' : '10px' 
37943                             }
37944                             
37945                         }, Roo.id());
37946                                 this.ds.load({});
37947                     }
37948                 },
37949                 autoWidth : true,
37950                 monitorWindowResize : false,
37951                 cellrenderer : function(v,x,r)
37952                 {
37953                     return v;
37954                 },
37955                 sm : {
37956                     xtype: 'CellSelectionModel',
37957                     xns: Roo.grid
37958                 },
37959                 dataSource : {
37960                     xtype: 'Store',
37961                     xns: Roo.data,
37962                     listeners : {
37963                         beforeload : function (_self, options)
37964                         {
37965                             options.params = options.params || {};
37966                             options.params._month = _this.monthField.getValue();
37967                             options.params.limit = 9999;
37968                             options.params['sort'] = 'when_dt';    
37969                             options.params['dir'] = 'ASC';    
37970                             this.proxy.loadResponse = this.loadResponse;
37971                             Roo.log("load?");
37972                             //this.addColumns();
37973                         },
37974                         load : function (_self, records, options)
37975                         {
37976                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
37977                                 // if you click on the translation.. you can edit it...
37978                                 var el = Roo.get(this);
37979                                 var id = el.dom.getAttribute('data-id');
37980                                 var d = el.dom.getAttribute('data-date');
37981                                 var t = el.dom.getAttribute('data-time');
37982                                 //var id = this.child('span').dom.textContent;
37983                                 
37984                                 //Roo.log(this);
37985                                 Pman.Dialog.CourseCalendar.show({
37986                                     id : id,
37987                                     when_d : d,
37988                                     when_t : t,
37989                                     productitem_active : id ? 1 : 0
37990                                 }, function() {
37991                                     _this.grid.ds.load({});
37992                                 });
37993                            
37994                            });
37995                            
37996                            _this.panel.fireEvent('resize', [ '', '' ]);
37997                         }
37998                     },
37999                     loadResponse : function(o, success, response){
38000                             // this is overridden on before load..
38001                             
38002                             Roo.log("our code?");       
38003                             //Roo.log(success);
38004                             //Roo.log(response)
38005                             delete this.activeRequest;
38006                             if(!success){
38007                                 this.fireEvent("loadexception", this, o, response);
38008                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38009                                 return;
38010                             }
38011                             var result;
38012                             try {
38013                                 result = o.reader.read(response);
38014                             }catch(e){
38015                                 Roo.log("load exception?");
38016                                 this.fireEvent("loadexception", this, o, response, e);
38017                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38018                                 return;
38019                             }
38020                             Roo.log("ready...");        
38021                             // loop through result.records;
38022                             // and set this.tdate[date] = [] << array of records..
38023                             _this.tdata  = {};
38024                             Roo.each(result.records, function(r){
38025                                 //Roo.log(r.data);
38026                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38027                                     _this.tdata[r.data.when_dt.format('j')] = [];
38028                                 }
38029                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38030                             });
38031                             
38032                             //Roo.log(_this.tdata);
38033                             
38034                             result.records = [];
38035                             result.totalRecords = 6;
38036                     
38037                             // let's generate some duumy records for the rows.
38038                             //var st = _this.dateField.getValue();
38039                             
38040                             // work out monday..
38041                             //st = st.add(Date.DAY, -1 * st.format('w'));
38042                             
38043                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38044                             
38045                             var firstOfMonth = date.getFirstDayOfMonth();
38046                             var days = date.getDaysInMonth();
38047                             var d = 1;
38048                             var firstAdded = false;
38049                             for (var i = 0; i < result.totalRecords ; i++) {
38050                                 //var d= st.add(Date.DAY, i);
38051                                 var row = {};
38052                                 var added = 0;
38053                                 for(var w = 0 ; w < 7 ; w++){
38054                                     if(!firstAdded && firstOfMonth != w){
38055                                         continue;
38056                                     }
38057                                     if(d > days){
38058                                         continue;
38059                                     }
38060                                     firstAdded = true;
38061                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
38062                                     row['weekday'+w] = String.format(
38063                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
38064                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38065                                                     d,
38066                                                     date.format('Y-m-')+dd
38067                                                 );
38068                                     added++;
38069                                     if(typeof(_this.tdata[d]) != 'undefined'){
38070                                         Roo.each(_this.tdata[d], function(r){
38071                                             var is_sub = '';
38072                                             var deactive = '';
38073                                             var id = r.id;
38074                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38075                                             if(r.parent_id*1>0){
38076                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38077                                                 id = r.parent_id;
38078                                             }
38079                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38080                                                 deactive = 'de-act-link';
38081                                             }
38082                                             
38083                                             row['weekday'+w] += String.format(
38084                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38085                                                     id, //0
38086                                                     r.product_id_name, //1
38087                                                     r.when_dt.format('h:ia'), //2
38088                                                     is_sub, //3
38089                                                     deactive, //4
38090                                                     desc // 5
38091                                             );
38092                                         });
38093                                     }
38094                                     d++;
38095                                 }
38096                                 
38097                                 // only do this if something added..
38098                                 if(added > 0){ 
38099                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38100                                 }
38101                                 
38102                                 
38103                                 // push it twice. (second one with an hour..
38104                                 
38105                             }
38106                             //Roo.log(result);
38107                             this.fireEvent("load", this, o, o.request.arg);
38108                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38109                         },
38110                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38111                     proxy : {
38112                         xtype: 'HttpProxy',
38113                         xns: Roo.data,
38114                         method : 'GET',
38115                         url : baseURL + '/Roo/Shop_course.php'
38116                     },
38117                     reader : {
38118                         xtype: 'JsonReader',
38119                         xns: Roo.data,
38120                         id : 'id',
38121                         fields : [
38122                             {
38123                                 'name': 'id',
38124                                 'type': 'int'
38125                             },
38126                             {
38127                                 'name': 'when_dt',
38128                                 'type': 'string'
38129                             },
38130                             {
38131                                 'name': 'end_dt',
38132                                 'type': 'string'
38133                             },
38134                             {
38135                                 'name': 'parent_id',
38136                                 'type': 'int'
38137                             },
38138                             {
38139                                 'name': 'product_id',
38140                                 'type': 'int'
38141                             },
38142                             {
38143                                 'name': 'productitem_id',
38144                                 'type': 'int'
38145                             },
38146                             {
38147                                 'name': 'guid',
38148                                 'type': 'int'
38149                             }
38150                         ]
38151                     }
38152                 },
38153                 toolbar : {
38154                     xtype: 'Toolbar',
38155                     xns: Roo,
38156                     items : [
38157                         {
38158                             xtype: 'Button',
38159                             xns: Roo.Toolbar,
38160                             listeners : {
38161                                 click : function (_self, e)
38162                                 {
38163                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38164                                     sd.setMonth(sd.getMonth()-1);
38165                                     _this.monthField.setValue(sd.format('Y-m-d'));
38166                                     _this.grid.ds.load({});
38167                                 }
38168                             },
38169                             text : "Back"
38170                         },
38171                         {
38172                             xtype: 'Separator',
38173                             xns: Roo.Toolbar
38174                         },
38175                         {
38176                             xtype: 'MonthField',
38177                             xns: Roo.form,
38178                             listeners : {
38179                                 render : function (_self)
38180                                 {
38181                                     _this.monthField = _self;
38182                                    // _this.monthField.set  today
38183                                 },
38184                                 select : function (combo, date)
38185                                 {
38186                                     _this.grid.ds.load({});
38187                                 }
38188                             },
38189                             value : (function() { return new Date(); })()
38190                         },
38191                         {
38192                             xtype: 'Separator',
38193                             xns: Roo.Toolbar
38194                         },
38195                         {
38196                             xtype: 'TextItem',
38197                             xns: Roo.Toolbar,
38198                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38199                         },
38200                         {
38201                             xtype: 'Fill',
38202                             xns: Roo.Toolbar
38203                         },
38204                         {
38205                             xtype: 'Button',
38206                             xns: Roo.Toolbar,
38207                             listeners : {
38208                                 click : function (_self, e)
38209                                 {
38210                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38211                                     sd.setMonth(sd.getMonth()+1);
38212                                     _this.monthField.setValue(sd.format('Y-m-d'));
38213                                     _this.grid.ds.load({});
38214                                 }
38215                             },
38216                             text : "Next"
38217                         }
38218                     ]
38219                 },
38220                  
38221             }
38222         };
38223         
38224         *//*
38225  * Based on:
38226  * Ext JS Library 1.1.1
38227  * Copyright(c) 2006-2007, Ext JS, LLC.
38228  *
38229  * Originally Released Under LGPL - original licence link has changed is not relivant.
38230  *
38231  * Fork - LGPL
38232  * <script type="text/javascript">
38233  */
38234  
38235 /**
38236  * @class Roo.LoadMask
38237  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38238  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38239  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38240  * element's UpdateManager load indicator and will be destroyed after the initial load.
38241  * @constructor
38242  * Create a new LoadMask
38243  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38244  * @param {Object} config The config object
38245  */
38246 Roo.LoadMask = function(el, config){
38247     this.el = Roo.get(el);
38248     Roo.apply(this, config);
38249     if(this.store){
38250         this.store.on('beforeload', this.onBeforeLoad, this);
38251         this.store.on('load', this.onLoad, this);
38252         this.store.on('loadexception', this.onLoadException, this);
38253         this.removeMask = false;
38254     }else{
38255         var um = this.el.getUpdateManager();
38256         um.showLoadIndicator = false; // disable the default indicator
38257         um.on('beforeupdate', this.onBeforeLoad, this);
38258         um.on('update', this.onLoad, this);
38259         um.on('failure', this.onLoad, this);
38260         this.removeMask = true;
38261     }
38262 };
38263
38264 Roo.LoadMask.prototype = {
38265     /**
38266      * @cfg {Boolean} removeMask
38267      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38268      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38269      */
38270     /**
38271      * @cfg {String} msg
38272      * The text to display in a centered loading message box (defaults to 'Loading...')
38273      */
38274     msg : 'Loading...',
38275     /**
38276      * @cfg {String} msgCls
38277      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38278      */
38279     msgCls : 'x-mask-loading',
38280
38281     /**
38282      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38283      * @type Boolean
38284      */
38285     disabled: false,
38286
38287     /**
38288      * Disables the mask to prevent it from being displayed
38289      */
38290     disable : function(){
38291        this.disabled = true;
38292     },
38293
38294     /**
38295      * Enables the mask so that it can be displayed
38296      */
38297     enable : function(){
38298         this.disabled = false;
38299     },
38300     
38301     onLoadException : function()
38302     {
38303         Roo.log(arguments);
38304         
38305         if (typeof(arguments[3]) != 'undefined') {
38306             Roo.MessageBox.alert("Error loading",arguments[3]);
38307         } 
38308         /*
38309         try {
38310             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38311                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38312             }   
38313         } catch(e) {
38314             
38315         }
38316         */
38317     
38318         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38319     },
38320     // private
38321     onLoad : function()
38322     {
38323         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38324     },
38325
38326     // private
38327     onBeforeLoad : function(){
38328         if(!this.disabled){
38329             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38330         }
38331     },
38332
38333     // private
38334     destroy : function(){
38335         if(this.store){
38336             this.store.un('beforeload', this.onBeforeLoad, this);
38337             this.store.un('load', this.onLoad, this);
38338             this.store.un('loadexception', this.onLoadException, this);
38339         }else{
38340             var um = this.el.getUpdateManager();
38341             um.un('beforeupdate', this.onBeforeLoad, this);
38342             um.un('update', this.onLoad, this);
38343             um.un('failure', this.onLoad, this);
38344         }
38345     }
38346 };/*
38347  * Based on:
38348  * Ext JS Library 1.1.1
38349  * Copyright(c) 2006-2007, Ext JS, LLC.
38350  *
38351  * Originally Released Under LGPL - original licence link has changed is not relivant.
38352  *
38353  * Fork - LGPL
38354  * <script type="text/javascript">
38355  */
38356
38357
38358 /**
38359  * @class Roo.XTemplate
38360  * @extends Roo.Template
38361  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38362 <pre><code>
38363 var t = new Roo.XTemplate(
38364         '&lt;select name="{name}"&gt;',
38365                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38366         '&lt;/select&gt;'
38367 );
38368  
38369 // then append, applying the master template values
38370  </code></pre>
38371  *
38372  * Supported features:
38373  *
38374  *  Tags:
38375
38376 <pre><code>
38377       {a_variable} - output encoded.
38378       {a_variable.format:("Y-m-d")} - call a method on the variable
38379       {a_variable:raw} - unencoded output
38380       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38381       {a_variable:this.method_on_template(...)} - call a method on the template object.
38382  
38383 </code></pre>
38384  *  The tpl tag:
38385 <pre><code>
38386         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38387         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38388         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38389         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38390   
38391         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38392         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38393 </code></pre>
38394  *      
38395  */
38396 Roo.XTemplate = function()
38397 {
38398     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38399     if (this.html) {
38400         this.compile();
38401     }
38402 };
38403
38404
38405 Roo.extend(Roo.XTemplate, Roo.Template, {
38406
38407     /**
38408      * The various sub templates
38409      */
38410     tpls : false,
38411     /**
38412      *
38413      * basic tag replacing syntax
38414      * WORD:WORD()
38415      *
38416      * // you can fake an object call by doing this
38417      *  x.t:(test,tesT) 
38418      * 
38419      */
38420     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38421
38422     /**
38423      * compile the template
38424      *
38425      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38426      *
38427      */
38428     compile: function()
38429     {
38430         var s = this.html;
38431      
38432         s = ['<tpl>', s, '</tpl>'].join('');
38433     
38434         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38435             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38436             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38437             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38438             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38439             m,
38440             id     = 0,
38441             tpls   = [];
38442     
38443         while(true == !!(m = s.match(re))){
38444             var forMatch   = m[0].match(nameRe),
38445                 ifMatch   = m[0].match(ifRe),
38446                 execMatch   = m[0].match(execRe),
38447                 namedMatch   = m[0].match(namedRe),
38448                 
38449                 exp  = null, 
38450                 fn   = null,
38451                 exec = null,
38452                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38453                 
38454             if (ifMatch) {
38455                 // if - puts fn into test..
38456                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38457                 if(exp){
38458                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38459                 }
38460             }
38461             
38462             if (execMatch) {
38463                 // exec - calls a function... returns empty if true is  returned.
38464                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38465                 if(exp){
38466                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38467                 }
38468             }
38469             
38470             
38471             if (name) {
38472                 // for = 
38473                 switch(name){
38474                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38475                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38476                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38477                 }
38478             }
38479             var uid = namedMatch ? namedMatch[1] : id;
38480             
38481             
38482             tpls.push({
38483                 id:     namedMatch ? namedMatch[1] : id,
38484                 target: name,
38485                 exec:   exec,
38486                 test:   fn,
38487                 body:   m[1] || ''
38488             });
38489             if (namedMatch) {
38490                 s = s.replace(m[0], '');
38491             } else { 
38492                 s = s.replace(m[0], '{xtpl'+ id + '}');
38493             }
38494             ++id;
38495         }
38496         this.tpls = [];
38497         for(var i = tpls.length-1; i >= 0; --i){
38498             this.compileTpl(tpls[i]);
38499             this.tpls[tpls[i].id] = tpls[i];
38500         }
38501         this.master = tpls[tpls.length-1];
38502         return this;
38503     },
38504     /**
38505      * same as applyTemplate, except it's done to one of the subTemplates
38506      * when using named templates, you can do:
38507      *
38508      * var str = pl.applySubTemplate('your-name', values);
38509      *
38510      * 
38511      * @param {Number} id of the template
38512      * @param {Object} values to apply to template
38513      * @param {Object} parent (normaly the instance of this object)
38514      */
38515     applySubTemplate : function(id, values, parent)
38516     {
38517         
38518         
38519         var t = this.tpls[id];
38520         
38521         
38522         try { 
38523             if(t.test && !t.test.call(this, values, parent)){
38524                 return '';
38525             }
38526         } catch(e) {
38527             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38528             Roo.log(e.toString());
38529             Roo.log(t.test);
38530             return ''
38531         }
38532         try { 
38533             
38534             if(t.exec && t.exec.call(this, values, parent)){
38535                 return '';
38536             }
38537         } catch(e) {
38538             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38539             Roo.log(e.toString());
38540             Roo.log(t.exec);
38541             return ''
38542         }
38543         try {
38544             var vs = t.target ? t.target.call(this, values, parent) : values;
38545             parent = t.target ? values : parent;
38546             if(t.target && vs instanceof Array){
38547                 var buf = [];
38548                 for(var i = 0, len = vs.length; i < len; i++){
38549                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38550                 }
38551                 return buf.join('');
38552             }
38553             return t.compiled.call(this, vs, parent);
38554         } catch (e) {
38555             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38556             Roo.log(e.toString());
38557             Roo.log(t.compiled);
38558             return '';
38559         }
38560     },
38561
38562     compileTpl : function(tpl)
38563     {
38564         var fm = Roo.util.Format;
38565         var useF = this.disableFormats !== true;
38566         var sep = Roo.isGecko ? "+" : ",";
38567         var undef = function(str) {
38568             Roo.log("Property not found :"  + str);
38569             return '';
38570         };
38571         
38572         var fn = function(m, name, format, args)
38573         {
38574             //Roo.log(arguments);
38575             args = args ? args.replace(/\\'/g,"'") : args;
38576             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38577             if (typeof(format) == 'undefined') {
38578                 format= 'htmlEncode';
38579             }
38580             if (format == 'raw' ) {
38581                 format = false;
38582             }
38583             
38584             if(name.substr(0, 4) == 'xtpl'){
38585                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38586             }
38587             
38588             // build an array of options to determine if value is undefined..
38589             
38590             // basically get 'xxxx.yyyy' then do
38591             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38592             //    (function () { Roo.log("Property not found"); return ''; })() :
38593             //    ......
38594             
38595             var udef_ar = [];
38596             var lookfor = '';
38597             Roo.each(name.split('.'), function(st) {
38598                 lookfor += (lookfor.length ? '.': '') + st;
38599                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38600             });
38601             
38602             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38603             
38604             
38605             if(format && useF){
38606                 
38607                 args = args ? ',' + args : "";
38608                  
38609                 if(format.substr(0, 5) != "this."){
38610                     format = "fm." + format + '(';
38611                 }else{
38612                     format = 'this.call("'+ format.substr(5) + '", ';
38613                     args = ", values";
38614                 }
38615                 
38616                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38617             }
38618              
38619             if (args.length) {
38620                 // called with xxyx.yuu:(test,test)
38621                 // change to ()
38622                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38623             }
38624             // raw.. - :raw modifier..
38625             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38626             
38627         };
38628         var body;
38629         // branched to use + in gecko and [].join() in others
38630         if(Roo.isGecko){
38631             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38632                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38633                     "';};};";
38634         }else{
38635             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38636             body.push(tpl.body.replace(/(\r\n|\n)/g,
38637                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38638             body.push("'].join('');};};");
38639             body = body.join('');
38640         }
38641         
38642         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38643        
38644         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38645         eval(body);
38646         
38647         return this;
38648     },
38649
38650     applyTemplate : function(values){
38651         return this.master.compiled.call(this, values, {});
38652         //var s = this.subs;
38653     },
38654
38655     apply : function(){
38656         return this.applyTemplate.apply(this, arguments);
38657     }
38658
38659  });
38660
38661 Roo.XTemplate.from = function(el){
38662     el = Roo.getDom(el);
38663     return new Roo.XTemplate(el.value || el.innerHTML);
38664 };