Fix #6893 - fix roo docs
[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     mode: false,
3059     /**
3060      * @cfg {String} offset
3061      * The number of pixels to offset the shadow from the element (defaults to 4)
3062      */
3063     offset: 4,
3064
3065     // private
3066     defaultMode: "drop",
3067
3068     /**
3069      * Displays the shadow under the target element
3070      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3071      */
3072     show : function(target){
3073         target = Roo.get(target);
3074         if(!this.el){
3075             this.el = Roo.Shadow.Pool.pull();
3076             if(this.el.dom.nextSibling != target.dom){
3077                 this.el.insertBefore(target);
3078             }
3079         }
3080         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3081         if(Roo.isIE){
3082             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3083         }
3084         this.realign(
3085             target.getLeft(true),
3086             target.getTop(true),
3087             target.getWidth(),
3088             target.getHeight()
3089         );
3090         this.el.dom.style.display = "block";
3091     },
3092
3093     /**
3094      * Returns true if the shadow is visible, else false
3095      */
3096     isVisible : function(){
3097         return this.el ? true : false;  
3098     },
3099
3100     /**
3101      * Direct alignment when values are already available. Show must be called at least once before
3102      * calling this method to ensure it is initialized.
3103      * @param {Number} left The target element left position
3104      * @param {Number} top The target element top position
3105      * @param {Number} width The target element width
3106      * @param {Number} height The target element height
3107      */
3108     realign : function(l, t, w, h){
3109         if(!this.el){
3110             return;
3111         }
3112         var a = this.adjusts, d = this.el.dom, s = d.style;
3113         var iea = 0;
3114         s.left = (l+a.l)+"px";
3115         s.top = (t+a.t)+"px";
3116         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3117  
3118         if(s.width != sws || s.height != shs){
3119             s.width = sws;
3120             s.height = shs;
3121             if(!Roo.isIE){
3122                 var cn = d.childNodes;
3123                 var sww = Math.max(0, (sw-12))+"px";
3124                 cn[0].childNodes[1].style.width = sww;
3125                 cn[1].childNodes[1].style.width = sww;
3126                 cn[2].childNodes[1].style.width = sww;
3127                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3128             }
3129         }
3130     },
3131
3132     /**
3133      * Hides this shadow
3134      */
3135     hide : function(){
3136         if(this.el){
3137             this.el.dom.style.display = "none";
3138             Roo.Shadow.Pool.push(this.el);
3139             delete this.el;
3140         }
3141     },
3142
3143     /**
3144      * Adjust the z-index of this shadow
3145      * @param {Number} zindex The new z-index
3146      */
3147     setZIndex : function(z){
3148         this.zIndex = z;
3149         if(this.el){
3150             this.el.setStyle("z-index", z);
3151         }
3152     }
3153 };
3154
3155 // Private utility class that manages the internal Shadow cache
3156 Roo.Shadow.Pool = function(){
3157     var p = [];
3158     var markup = Roo.isIE ?
3159                  '<div class="x-ie-shadow"></div>' :
3160                  '<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>';
3161     return {
3162         pull : function(){
3163             var sh = p.shift();
3164             if(!sh){
3165                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3166                 sh.autoBoxAdjust = false;
3167             }
3168             return sh;
3169         },
3170
3171         push : function(sh){
3172             p.push(sh);
3173         }
3174     };
3175 }();/*
3176  * Based on:
3177  * Ext JS Library 1.1.1
3178  * Copyright(c) 2006-2007, Ext JS, LLC.
3179  *
3180  * Originally Released Under LGPL - original licence link has changed is not relivant.
3181  *
3182  * Fork - LGPL
3183  * <script type="text/javascript">
3184  */
3185
3186
3187 /**
3188  * @class Roo.SplitBar
3189  * @extends Roo.util.Observable
3190  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3191  * <br><br>
3192  * Usage:
3193  * <pre><code>
3194 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3195                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3196 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3197 split.minSize = 100;
3198 split.maxSize = 600;
3199 split.animate = true;
3200 split.on('moved', splitterMoved);
3201 </code></pre>
3202  * @constructor
3203  * Create a new SplitBar
3204  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3205  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3206  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3207  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3208                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3209                         position of the SplitBar).
3210  */
3211 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3212     
3213     /** @private */
3214     this.el = Roo.get(dragElement, true);
3215     this.el.dom.unselectable = "on";
3216     /** @private */
3217     this.resizingEl = Roo.get(resizingElement, true);
3218
3219     /**
3220      * @private
3221      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3222      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3223      * @type Number
3224      */
3225     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3226     
3227     /**
3228      * The minimum size of the resizing element. (Defaults to 0)
3229      * @type Number
3230      */
3231     this.minSize = 0;
3232     
3233     /**
3234      * The maximum size of the resizing element. (Defaults to 2000)
3235      * @type Number
3236      */
3237     this.maxSize = 2000;
3238     
3239     /**
3240      * Whether to animate the transition to the new size
3241      * @type Boolean
3242      */
3243     this.animate = false;
3244     
3245     /**
3246      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3247      * @type Boolean
3248      */
3249     this.useShim = false;
3250     
3251     /** @private */
3252     this.shim = null;
3253     
3254     if(!existingProxy){
3255         /** @private */
3256         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3257     }else{
3258         this.proxy = Roo.get(existingProxy).dom;
3259     }
3260     /** @private */
3261     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3262     
3263     /** @private */
3264     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3265     
3266     /** @private */
3267     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3268     
3269     /** @private */
3270     this.dragSpecs = {};
3271     
3272     /**
3273      * @private The adapter to use to positon and resize elements
3274      */
3275     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3276     this.adapter.init(this);
3277     
3278     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3279         /** @private */
3280         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3281         this.el.addClass("x-splitbar-h");
3282     }else{
3283         /** @private */
3284         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3285         this.el.addClass("x-splitbar-v");
3286     }
3287     
3288     this.addEvents({
3289         /**
3290          * @event resize
3291          * Fires when the splitter is moved (alias for {@link #event-moved})
3292          * @param {Roo.SplitBar} this
3293          * @param {Number} newSize the new width or height
3294          */
3295         "resize" : true,
3296         /**
3297          * @event moved
3298          * Fires when the splitter is moved
3299          * @param {Roo.SplitBar} this
3300          * @param {Number} newSize the new width or height
3301          */
3302         "moved" : true,
3303         /**
3304          * @event beforeresize
3305          * Fires before the splitter is dragged
3306          * @param {Roo.SplitBar} this
3307          */
3308         "beforeresize" : true,
3309
3310         "beforeapply" : true
3311     });
3312
3313     Roo.util.Observable.call(this);
3314 };
3315
3316 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3317     onStartProxyDrag : function(x, y){
3318         this.fireEvent("beforeresize", this);
3319         if(!this.overlay){
3320             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3321             o.unselectable();
3322             o.enableDisplayMode("block");
3323             // all splitbars share the same overlay
3324             Roo.SplitBar.prototype.overlay = o;
3325         }
3326         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3327         this.overlay.show();
3328         Roo.get(this.proxy).setDisplayed("block");
3329         var size = this.adapter.getElementSize(this);
3330         this.activeMinSize = this.getMinimumSize();;
3331         this.activeMaxSize = this.getMaximumSize();;
3332         var c1 = size - this.activeMinSize;
3333         var c2 = Math.max(this.activeMaxSize - size, 0);
3334         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3335             this.dd.resetConstraints();
3336             this.dd.setXConstraint(
3337                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3338                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3339             );
3340             this.dd.setYConstraint(0, 0);
3341         }else{
3342             this.dd.resetConstraints();
3343             this.dd.setXConstraint(0, 0);
3344             this.dd.setYConstraint(
3345                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3346                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3347             );
3348          }
3349         this.dragSpecs.startSize = size;
3350         this.dragSpecs.startPoint = [x, y];
3351         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3352     },
3353     
3354     /** 
3355      * @private Called after the drag operation by the DDProxy
3356      */
3357     onEndProxyDrag : function(e){
3358         Roo.get(this.proxy).setDisplayed(false);
3359         var endPoint = Roo.lib.Event.getXY(e);
3360         if(this.overlay){
3361             this.overlay.hide();
3362         }
3363         var newSize;
3364         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3365             newSize = this.dragSpecs.startSize + 
3366                 (this.placement == Roo.SplitBar.LEFT ?
3367                     endPoint[0] - this.dragSpecs.startPoint[0] :
3368                     this.dragSpecs.startPoint[0] - endPoint[0]
3369                 );
3370         }else{
3371             newSize = this.dragSpecs.startSize + 
3372                 (this.placement == Roo.SplitBar.TOP ?
3373                     endPoint[1] - this.dragSpecs.startPoint[1] :
3374                     this.dragSpecs.startPoint[1] - endPoint[1]
3375                 );
3376         }
3377         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3378         if(newSize != this.dragSpecs.startSize){
3379             if(this.fireEvent('beforeapply', this, newSize) !== false){
3380                 this.adapter.setElementSize(this, newSize);
3381                 this.fireEvent("moved", this, newSize);
3382                 this.fireEvent("resize", this, newSize);
3383             }
3384         }
3385     },
3386     
3387     /**
3388      * Get the adapter this SplitBar uses
3389      * @return The adapter object
3390      */
3391     getAdapter : function(){
3392         return this.adapter;
3393     },
3394     
3395     /**
3396      * Set the adapter this SplitBar uses
3397      * @param {Object} adapter A SplitBar adapter object
3398      */
3399     setAdapter : function(adapter){
3400         this.adapter = adapter;
3401         this.adapter.init(this);
3402     },
3403     
3404     /**
3405      * Gets the minimum size for the resizing element
3406      * @return {Number} The minimum size
3407      */
3408     getMinimumSize : function(){
3409         return this.minSize;
3410     },
3411     
3412     /**
3413      * Sets the minimum size for the resizing element
3414      * @param {Number} minSize The minimum size
3415      */
3416     setMinimumSize : function(minSize){
3417         this.minSize = minSize;
3418     },
3419     
3420     /**
3421      * Gets the maximum size for the resizing element
3422      * @return {Number} The maximum size
3423      */
3424     getMaximumSize : function(){
3425         return this.maxSize;
3426     },
3427     
3428     /**
3429      * Sets the maximum size for the resizing element
3430      * @param {Number} maxSize The maximum size
3431      */
3432     setMaximumSize : function(maxSize){
3433         this.maxSize = maxSize;
3434     },
3435     
3436     /**
3437      * Sets the initialize size for the resizing element
3438      * @param {Number} size The initial size
3439      */
3440     setCurrentSize : function(size){
3441         var oldAnimate = this.animate;
3442         this.animate = false;
3443         this.adapter.setElementSize(this, size);
3444         this.animate = oldAnimate;
3445     },
3446     
3447     /**
3448      * Destroy this splitbar. 
3449      * @param {Boolean} removeEl True to remove the element
3450      */
3451     destroy : function(removeEl){
3452         if(this.shim){
3453             this.shim.remove();
3454         }
3455         this.dd.unreg();
3456         this.proxy.parentNode.removeChild(this.proxy);
3457         if(removeEl){
3458             this.el.remove();
3459         }
3460     }
3461 });
3462
3463 /**
3464  * @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.
3465  */
3466 Roo.SplitBar.createProxy = function(dir){
3467     var proxy = new Roo.Element(document.createElement("div"));
3468     proxy.unselectable();
3469     var cls = 'x-splitbar-proxy';
3470     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3471     document.body.appendChild(proxy.dom);
3472     return proxy.dom;
3473 };
3474
3475 /** 
3476  * @class Roo.SplitBar.BasicLayoutAdapter
3477  * Default Adapter. It assumes the splitter and resizing element are not positioned
3478  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3479  */
3480 Roo.SplitBar.BasicLayoutAdapter = function(){
3481 };
3482
3483 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3484     // do nothing for now
3485     init : function(s){
3486     
3487     },
3488     /**
3489      * Called before drag operations to get the current size of the resizing element. 
3490      * @param {Roo.SplitBar} s The SplitBar using this adapter
3491      */
3492      getElementSize : function(s){
3493         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3494             return s.resizingEl.getWidth();
3495         }else{
3496             return s.resizingEl.getHeight();
3497         }
3498     },
3499     
3500     /**
3501      * Called after drag operations to set the size of the resizing element.
3502      * @param {Roo.SplitBar} s The SplitBar using this adapter
3503      * @param {Number} newSize The new size to set
3504      * @param {Function} onComplete A function to be invoked when resizing is complete
3505      */
3506     setElementSize : function(s, newSize, onComplete){
3507         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3508             if(!s.animate){
3509                 s.resizingEl.setWidth(newSize);
3510                 if(onComplete){
3511                     onComplete(s, newSize);
3512                 }
3513             }else{
3514                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3515             }
3516         }else{
3517             
3518             if(!s.animate){
3519                 s.resizingEl.setHeight(newSize);
3520                 if(onComplete){
3521                     onComplete(s, newSize);
3522                 }
3523             }else{
3524                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3525             }
3526         }
3527     }
3528 };
3529
3530 /** 
3531  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3532  * @extends Roo.SplitBar.BasicLayoutAdapter
3533  * Adapter that  moves the splitter element to align with the resized sizing element. 
3534  * Used with an absolute positioned SplitBar.
3535  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3536  * document.body, make sure you assign an id to the body element.
3537  */
3538 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3539     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3540     this.container = Roo.get(container);
3541 };
3542
3543 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3544     init : function(s){
3545         this.basic.init(s);
3546     },
3547     
3548     getElementSize : function(s){
3549         return this.basic.getElementSize(s);
3550     },
3551     
3552     setElementSize : function(s, newSize, onComplete){
3553         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3554     },
3555     
3556     moveSplitter : function(s){
3557         var yes = Roo.SplitBar;
3558         switch(s.placement){
3559             case yes.LEFT:
3560                 s.el.setX(s.resizingEl.getRight());
3561                 break;
3562             case yes.RIGHT:
3563                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3564                 break;
3565             case yes.TOP:
3566                 s.el.setY(s.resizingEl.getBottom());
3567                 break;
3568             case yes.BOTTOM:
3569                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3570                 break;
3571         }
3572     }
3573 };
3574
3575 /**
3576  * Orientation constant - Create a vertical SplitBar
3577  * @static
3578  * @type Number
3579  */
3580 Roo.SplitBar.VERTICAL = 1;
3581
3582 /**
3583  * Orientation constant - Create a horizontal SplitBar
3584  * @static
3585  * @type Number
3586  */
3587 Roo.SplitBar.HORIZONTAL = 2;
3588
3589 /**
3590  * Placement constant - The resizing element is to the left of the splitter element
3591  * @static
3592  * @type Number
3593  */
3594 Roo.SplitBar.LEFT = 1;
3595
3596 /**
3597  * Placement constant - The resizing element is to the right of the splitter element
3598  * @static
3599  * @type Number
3600  */
3601 Roo.SplitBar.RIGHT = 2;
3602
3603 /**
3604  * Placement constant - The resizing element is positioned above the splitter element
3605  * @static
3606  * @type Number
3607  */
3608 Roo.SplitBar.TOP = 3;
3609
3610 /**
3611  * Placement constant - The resizing element is positioned under splitter element
3612  * @static
3613  * @type Number
3614  */
3615 Roo.SplitBar.BOTTOM = 4;
3616 /*
3617  * Based on:
3618  * Ext JS Library 1.1.1
3619  * Copyright(c) 2006-2007, Ext JS, LLC.
3620  *
3621  * Originally Released Under LGPL - original licence link has changed is not relivant.
3622  *
3623  * Fork - LGPL
3624  * <script type="text/javascript">
3625  */
3626
3627 /**
3628  * @class Roo.View
3629  * @extends Roo.util.Observable
3630  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
3631  * This class also supports single and multi selection modes. <br>
3632  * Create a data model bound view:
3633  <pre><code>
3634  var store = new Roo.data.Store(...);
3635
3636  var view = new Roo.View({
3637     el : "my-element",
3638     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
3639  
3640     singleSelect: true,
3641     selectedClass: "ydataview-selected",
3642     store: store
3643  });
3644
3645  // listen for node click?
3646  view.on("click", function(vw, index, node, e){
3647  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3648  });
3649
3650  // load XML data
3651  dataModel.load("foobar.xml");
3652  </code></pre>
3653  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3654  * <br><br>
3655  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3656  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3657  * 
3658  * Note: old style constructor is still suported (container, template, config)
3659  * 
3660  * @constructor
3661  * Create a new View
3662  * @param {Object} config The config object
3663  * 
3664  */
3665 Roo.View = function(config, depreciated_tpl, depreciated_config){
3666     
3667     this.parent = false;
3668     
3669     if (typeof(depreciated_tpl) == 'undefined') {
3670         // new way.. - universal constructor.
3671         Roo.apply(this, config);
3672         this.el  = Roo.get(this.el);
3673     } else {
3674         // old format..
3675         this.el  = Roo.get(config);
3676         this.tpl = depreciated_tpl;
3677         Roo.apply(this, depreciated_config);
3678     }
3679     this.wrapEl  = this.el.wrap().wrap();
3680     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3681     
3682     
3683     if(typeof(this.tpl) == "string"){
3684         this.tpl = new Roo.Template(this.tpl);
3685     } else {
3686         // support xtype ctors..
3687         this.tpl = new Roo.factory(this.tpl, Roo);
3688     }
3689     
3690     
3691     this.tpl.compile();
3692     
3693     /** @private */
3694     this.addEvents({
3695         /**
3696          * @event beforeclick
3697          * Fires before a click is processed. Returns false to cancel the default action.
3698          * @param {Roo.View} this
3699          * @param {Number} index The index of the target node
3700          * @param {HTMLElement} node The target node
3701          * @param {Roo.EventObject} e The raw event object
3702          */
3703             "beforeclick" : true,
3704         /**
3705          * @event click
3706          * Fires when a template node is clicked.
3707          * @param {Roo.View} this
3708          * @param {Number} index The index of the target node
3709          * @param {HTMLElement} node The target node
3710          * @param {Roo.EventObject} e The raw event object
3711          */
3712             "click" : true,
3713         /**
3714          * @event dblclick
3715          * Fires when a template node is double clicked.
3716          * @param {Roo.View} this
3717          * @param {Number} index The index of the target node
3718          * @param {HTMLElement} node The target node
3719          * @param {Roo.EventObject} e The raw event object
3720          */
3721             "dblclick" : true,
3722         /**
3723          * @event contextmenu
3724          * Fires when a template node is right clicked.
3725          * @param {Roo.View} this
3726          * @param {Number} index The index of the target node
3727          * @param {HTMLElement} node The target node
3728          * @param {Roo.EventObject} e The raw event object
3729          */
3730             "contextmenu" : true,
3731         /**
3732          * @event selectionchange
3733          * Fires when the selected nodes change.
3734          * @param {Roo.View} this
3735          * @param {Array} selections Array of the selected nodes
3736          */
3737             "selectionchange" : true,
3738     
3739         /**
3740          * @event beforeselect
3741          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3742          * @param {Roo.View} this
3743          * @param {HTMLElement} node The node to be selected
3744          * @param {Array} selections Array of currently selected nodes
3745          */
3746             "beforeselect" : true,
3747         /**
3748          * @event preparedata
3749          * Fires on every row to render, to allow you to change the data.
3750          * @param {Roo.View} this
3751          * @param {Object} data to be rendered (change this)
3752          */
3753           "preparedata" : true
3754           
3755           
3756         });
3757
3758
3759
3760     this.el.on({
3761         "click": this.onClick,
3762         "dblclick": this.onDblClick,
3763         "contextmenu": this.onContextMenu,
3764         scope:this
3765     });
3766
3767     this.selections = [];
3768     this.nodes = [];
3769     this.cmp = new Roo.CompositeElementLite([]);
3770     if(this.store){
3771         this.store = Roo.factory(this.store, Roo.data);
3772         this.setStore(this.store, true);
3773     }
3774     
3775     if ( this.footer && this.footer.xtype) {
3776            
3777          var fctr = this.wrapEl.appendChild(document.createElement("div"));
3778         
3779         this.footer.dataSource = this.store;
3780         this.footer.container = fctr;
3781         this.footer = Roo.factory(this.footer, Roo);
3782         fctr.insertFirst(this.el);
3783         
3784         // this is a bit insane - as the paging toolbar seems to detach the el..
3785 //        dom.parentNode.parentNode.parentNode
3786          // they get detached?
3787     }
3788     
3789     
3790     Roo.View.superclass.constructor.call(this);
3791     
3792     
3793 };
3794
3795 Roo.extend(Roo.View, Roo.util.Observable, {
3796     
3797      /**
3798      * @cfg {Roo.data.Store} store Data store to load data from.
3799      */
3800     store : false,
3801     
3802     /**
3803      * @cfg {String|Roo.Element} el The container element.
3804      */
3805     el : '',
3806     
3807     /**
3808      * @cfg {String|Roo.Template} tpl The template used by this View 
3809      */
3810     tpl : false,
3811     /**
3812      * @cfg {String} dataName the named area of the template to use as the data area
3813      *                          Works with domtemplates roo-name="name"
3814      */
3815     dataName: false,
3816     /**
3817      * @cfg {String} selectedClass The css class to add to selected nodes
3818      */
3819     selectedClass : "x-view-selected",
3820      /**
3821      * @cfg {String} emptyText The empty text to show when nothing is loaded.
3822      */
3823     emptyText : "",
3824     
3825     /**
3826      * @cfg {String} text to display on mask (default Loading)
3827      */
3828     mask : false,
3829     /**
3830      * @cfg {Boolean} multiSelect Allow multiple selection
3831      */
3832     multiSelect : false,
3833     /**
3834      * @cfg {Boolean} singleSelect Allow single selection
3835      */
3836     singleSelect:  false,
3837     
3838     /**
3839      * @cfg {Boolean} toggleSelect - selecting 
3840      */
3841     toggleSelect : false,
3842     
3843     /**
3844      * @cfg {Boolean} tickable - selecting 
3845      */
3846     tickable : false,
3847     
3848     /**
3849      * Returns the element this view is bound to.
3850      * @return {Roo.Element}
3851      */
3852     getEl : function(){
3853         return this.wrapEl;
3854     },
3855     
3856     
3857
3858     /**
3859      * Refreshes the view. - called by datachanged on the store. - do not call directly.
3860      */
3861     refresh : function(){
3862         //Roo.log('refresh');
3863         var t = this.tpl;
3864         
3865         // if we are using something like 'domtemplate', then
3866         // the what gets used is:
3867         // t.applySubtemplate(NAME, data, wrapping data..)
3868         // the outer template then get' applied with
3869         //     the store 'extra data'
3870         // and the body get's added to the
3871         //      roo-name="data" node?
3872         //      <span class='roo-tpl-{name}'></span> ?????
3873         
3874         
3875         
3876         this.clearSelections();
3877         this.el.update("");
3878         var html = [];
3879         var records = this.store.getRange();
3880         if(records.length < 1) {
3881             
3882             // is this valid??  = should it render a template??
3883             
3884             this.el.update(this.emptyText);
3885             return;
3886         }
3887         var el = this.el;
3888         if (this.dataName) {
3889             this.el.update(t.apply(this.store.meta)); //????
3890             el = this.el.child('.roo-tpl-' + this.dataName);
3891         }
3892         
3893         for(var i = 0, len = records.length; i < len; i++){
3894             var data = this.prepareData(records[i].data, i, records[i]);
3895             this.fireEvent("preparedata", this, data, i, records[i]);
3896             
3897             var d = Roo.apply({}, data);
3898             
3899             if(this.tickable){
3900                 Roo.apply(d, {'roo-id' : Roo.id()});
3901                 
3902                 var _this = this;
3903             
3904                 Roo.each(this.parent.item, function(item){
3905                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3906                         return;
3907                     }
3908                     Roo.apply(d, {'roo-data-checked' : 'checked'});
3909                 });
3910             }
3911             
3912             html[html.length] = Roo.util.Format.trim(
3913                 this.dataName ?
3914                     t.applySubtemplate(this.dataName, d, this.store.meta) :
3915                     t.apply(d)
3916             );
3917         }
3918         
3919         
3920         
3921         el.update(html.join(""));
3922         this.nodes = el.dom.childNodes;
3923         this.updateIndexes(0);
3924     },
3925     
3926
3927     /**
3928      * Function to override to reformat the data that is sent to
3929      * the template for each node.
3930      * DEPRICATED - use the preparedata event handler.
3931      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3932      * a JSON object for an UpdateManager bound view).
3933      */
3934     prepareData : function(data, index, record)
3935     {
3936         this.fireEvent("preparedata", this, data, index, record);
3937         return data;
3938     },
3939
3940     onUpdate : function(ds, record){
3941         // Roo.log('on update');   
3942         this.clearSelections();
3943         var index = this.store.indexOf(record);
3944         var n = this.nodes[index];
3945         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3946         n.parentNode.removeChild(n);
3947         this.updateIndexes(index, index);
3948     },
3949
3950     
3951     
3952 // --------- FIXME     
3953     onAdd : function(ds, records, index)
3954     {
3955         //Roo.log(['on Add', ds, records, index] );        
3956         this.clearSelections();
3957         if(this.nodes.length == 0){
3958             this.refresh();
3959             return;
3960         }
3961         var n = this.nodes[index];
3962         for(var i = 0, len = records.length; i < len; i++){
3963             var d = this.prepareData(records[i].data, i, records[i]);
3964             if(n){
3965                 this.tpl.insertBefore(n, d);
3966             }else{
3967                 
3968                 this.tpl.append(this.el, d);
3969             }
3970         }
3971         this.updateIndexes(index);
3972     },
3973
3974     onRemove : function(ds, record, index){
3975        // Roo.log('onRemove');
3976         this.clearSelections();
3977         var el = this.dataName  ?
3978             this.el.child('.roo-tpl-' + this.dataName) :
3979             this.el; 
3980         
3981         el.dom.removeChild(this.nodes[index]);
3982         this.updateIndexes(index);
3983     },
3984
3985     /**
3986      * Refresh an individual node.
3987      * @param {Number} index
3988      */
3989     refreshNode : function(index){
3990         this.onUpdate(this.store, this.store.getAt(index));
3991     },
3992
3993     updateIndexes : function(startIndex, endIndex){
3994         var ns = this.nodes;
3995         startIndex = startIndex || 0;
3996         endIndex = endIndex || ns.length - 1;
3997         for(var i = startIndex; i <= endIndex; i++){
3998             ns[i].nodeIndex = i;
3999         }
4000     },
4001
4002     /**
4003      * Changes the data store this view uses and refresh the view.
4004      * @param {Store} store
4005      */
4006     setStore : function(store, initial){
4007         if(!initial && this.store){
4008             this.store.un("datachanged", this.refresh);
4009             this.store.un("add", this.onAdd);
4010             this.store.un("remove", this.onRemove);
4011             this.store.un("update", this.onUpdate);
4012             this.store.un("clear", this.refresh);
4013             this.store.un("beforeload", this.onBeforeLoad);
4014             this.store.un("load", this.onLoad);
4015             this.store.un("loadexception", this.onLoad);
4016         }
4017         if(store){
4018           
4019             store.on("datachanged", this.refresh, this);
4020             store.on("add", this.onAdd, this);
4021             store.on("remove", this.onRemove, this);
4022             store.on("update", this.onUpdate, this);
4023             store.on("clear", this.refresh, this);
4024             store.on("beforeload", this.onBeforeLoad, this);
4025             store.on("load", this.onLoad, this);
4026             store.on("loadexception", this.onLoad, this);
4027         }
4028         
4029         if(store){
4030             this.refresh();
4031         }
4032     },
4033     /**
4034      * onbeforeLoad - masks the loading area.
4035      *
4036      */
4037     onBeforeLoad : function(store,opts)
4038     {
4039          //Roo.log('onBeforeLoad');   
4040         if (!opts.add) {
4041             this.el.update("");
4042         }
4043         this.el.mask(this.mask ? this.mask : "Loading" ); 
4044     },
4045     onLoad : function ()
4046     {
4047         this.el.unmask();
4048     },
4049     
4050
4051     /**
4052      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4053      * @param {HTMLElement} node
4054      * @return {HTMLElement} The template node
4055      */
4056     findItemFromChild : function(node){
4057         var el = this.dataName  ?
4058             this.el.child('.roo-tpl-' + this.dataName,true) :
4059             this.el.dom; 
4060         
4061         if(!node || node.parentNode == el){
4062                     return node;
4063             }
4064             var p = node.parentNode;
4065             while(p && p != el){
4066             if(p.parentNode == el){
4067                 return p;
4068             }
4069             p = p.parentNode;
4070         }
4071             return null;
4072     },
4073
4074     /** @ignore */
4075     onClick : function(e){
4076         var item = this.findItemFromChild(e.getTarget());
4077         if(item){
4078             var index = this.indexOf(item);
4079             if(this.onItemClick(item, index, e) !== false){
4080                 this.fireEvent("click", this, index, item, e);
4081             }
4082         }else{
4083             this.clearSelections();
4084         }
4085     },
4086
4087     /** @ignore */
4088     onContextMenu : function(e){
4089         var item = this.findItemFromChild(e.getTarget());
4090         if(item){
4091             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4092         }
4093     },
4094
4095     /** @ignore */
4096     onDblClick : function(e){
4097         var item = this.findItemFromChild(e.getTarget());
4098         if(item){
4099             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4100         }
4101     },
4102
4103     onItemClick : function(item, index, e)
4104     {
4105         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4106             return false;
4107         }
4108         if (this.toggleSelect) {
4109             var m = this.isSelected(item) ? 'unselect' : 'select';
4110             //Roo.log(m);
4111             var _t = this;
4112             _t[m](item, true, false);
4113             return true;
4114         }
4115         if(this.multiSelect || this.singleSelect){
4116             if(this.multiSelect && e.shiftKey && this.lastSelection){
4117                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4118             }else{
4119                 this.select(item, this.multiSelect && e.ctrlKey);
4120                 this.lastSelection = item;
4121             }
4122             
4123             if(!this.tickable){
4124                 e.preventDefault();
4125             }
4126             
4127         }
4128         return true;
4129     },
4130
4131     /**
4132      * Get the number of selected nodes.
4133      * @return {Number}
4134      */
4135     getSelectionCount : function(){
4136         return this.selections.length;
4137     },
4138
4139     /**
4140      * Get the currently selected nodes.
4141      * @return {Array} An array of HTMLElements
4142      */
4143     getSelectedNodes : function(){
4144         return this.selections;
4145     },
4146
4147     /**
4148      * Get the indexes of the selected nodes.
4149      * @return {Array}
4150      */
4151     getSelectedIndexes : function(){
4152         var indexes = [], s = this.selections;
4153         for(var i = 0, len = s.length; i < len; i++){
4154             indexes.push(s[i].nodeIndex);
4155         }
4156         return indexes;
4157     },
4158
4159     /**
4160      * Clear all selections
4161      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4162      */
4163     clearSelections : function(suppressEvent){
4164         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4165             this.cmp.elements = this.selections;
4166             this.cmp.removeClass(this.selectedClass);
4167             this.selections = [];
4168             if(!suppressEvent){
4169                 this.fireEvent("selectionchange", this, this.selections);
4170             }
4171         }
4172     },
4173
4174     /**
4175      * Returns true if the passed node is selected
4176      * @param {HTMLElement/Number} node The node or node index
4177      * @return {Boolean}
4178      */
4179     isSelected : function(node){
4180         var s = this.selections;
4181         if(s.length < 1){
4182             return false;
4183         }
4184         node = this.getNode(node);
4185         return s.indexOf(node) !== -1;
4186     },
4187
4188     /**
4189      * Selects nodes.
4190      * @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
4191      * @param {Boolean} keepExisting (optional) true to keep existing selections
4192      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4193      */
4194     select : function(nodeInfo, keepExisting, suppressEvent){
4195         if(nodeInfo instanceof Array){
4196             if(!keepExisting){
4197                 this.clearSelections(true);
4198             }
4199             for(var i = 0, len = nodeInfo.length; i < len; i++){
4200                 this.select(nodeInfo[i], true, true);
4201             }
4202             return;
4203         } 
4204         var node = this.getNode(nodeInfo);
4205         if(!node || this.isSelected(node)){
4206             return; // already selected.
4207         }
4208         if(!keepExisting){
4209             this.clearSelections(true);
4210         }
4211         
4212         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4213             Roo.fly(node).addClass(this.selectedClass);
4214             this.selections.push(node);
4215             if(!suppressEvent){
4216                 this.fireEvent("selectionchange", this, this.selections);
4217             }
4218         }
4219         
4220         
4221     },
4222       /**
4223      * Unselects nodes.
4224      * @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
4225      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4226      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4227      */
4228     unselect : function(nodeInfo, keepExisting, suppressEvent)
4229     {
4230         if(nodeInfo instanceof Array){
4231             Roo.each(this.selections, function(s) {
4232                 this.unselect(s, nodeInfo);
4233             }, this);
4234             return;
4235         }
4236         var node = this.getNode(nodeInfo);
4237         if(!node || !this.isSelected(node)){
4238             //Roo.log("not selected");
4239             return; // not selected.
4240         }
4241         // fireevent???
4242         var ns = [];
4243         Roo.each(this.selections, function(s) {
4244             if (s == node ) {
4245                 Roo.fly(node).removeClass(this.selectedClass);
4246
4247                 return;
4248             }
4249             ns.push(s);
4250         },this);
4251         
4252         this.selections= ns;
4253         this.fireEvent("selectionchange", this, this.selections);
4254     },
4255
4256     /**
4257      * Gets a template node.
4258      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4259      * @return {HTMLElement} The node or null if it wasn't found
4260      */
4261     getNode : function(nodeInfo){
4262         if(typeof nodeInfo == "string"){
4263             return document.getElementById(nodeInfo);
4264         }else if(typeof nodeInfo == "number"){
4265             return this.nodes[nodeInfo];
4266         }
4267         return nodeInfo;
4268     },
4269
4270     /**
4271      * Gets a range template nodes.
4272      * @param {Number} startIndex
4273      * @param {Number} endIndex
4274      * @return {Array} An array of nodes
4275      */
4276     getNodes : function(start, end){
4277         var ns = this.nodes;
4278         start = start || 0;
4279         end = typeof end == "undefined" ? ns.length - 1 : end;
4280         var nodes = [];
4281         if(start <= end){
4282             for(var i = start; i <= end; i++){
4283                 nodes.push(ns[i]);
4284             }
4285         } else{
4286             for(var i = start; i >= end; i--){
4287                 nodes.push(ns[i]);
4288             }
4289         }
4290         return nodes;
4291     },
4292
4293     /**
4294      * Finds the index of the passed node
4295      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4296      * @return {Number} The index of the node or -1
4297      */
4298     indexOf : function(node){
4299         node = this.getNode(node);
4300         if(typeof node.nodeIndex == "number"){
4301             return node.nodeIndex;
4302         }
4303         var ns = this.nodes;
4304         for(var i = 0, len = ns.length; i < len; i++){
4305             if(ns[i] == node){
4306                 return i;
4307             }
4308         }
4309         return -1;
4310     }
4311 });
4312 /*
4313  * Based on:
4314  * Ext JS Library 1.1.1
4315  * Copyright(c) 2006-2007, Ext JS, LLC.
4316  *
4317  * Originally Released Under LGPL - original licence link has changed is not relivant.
4318  *
4319  * Fork - LGPL
4320  * <script type="text/javascript">
4321  */
4322
4323 /**
4324  * @class Roo.JsonView
4325  * @extends Roo.View
4326  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4327 <pre><code>
4328 var view = new Roo.JsonView({
4329     container: "my-element",
4330     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4331     multiSelect: true, 
4332     jsonRoot: "data" 
4333 });
4334
4335 // listen for node click?
4336 view.on("click", function(vw, index, node, e){
4337     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4338 });
4339
4340 // direct load of JSON data
4341 view.load("foobar.php");
4342
4343 // Example from my blog list
4344 var tpl = new Roo.Template(
4345     '&lt;div class="entry"&gt;' +
4346     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4347     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4348     "&lt;/div&gt;&lt;hr /&gt;"
4349 );
4350
4351 var moreView = new Roo.JsonView({
4352     container :  "entry-list", 
4353     template : tpl,
4354     jsonRoot: "posts"
4355 });
4356 moreView.on("beforerender", this.sortEntries, this);
4357 moreView.load({
4358     url: "/blog/get-posts.php",
4359     params: "allposts=true",
4360     text: "Loading Blog Entries..."
4361 });
4362 </code></pre>
4363
4364 * Note: old code is supported with arguments : (container, template, config)
4365
4366
4367  * @constructor
4368  * Create a new JsonView
4369  * 
4370  * @param {Object} config The config object
4371  * 
4372  */
4373 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4374     
4375     
4376     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4377
4378     var um = this.el.getUpdateManager();
4379     um.setRenderer(this);
4380     um.on("update", this.onLoad, this);
4381     um.on("failure", this.onLoadException, this);
4382
4383     /**
4384      * @event beforerender
4385      * Fires before rendering of the downloaded JSON data.
4386      * @param {Roo.JsonView} this
4387      * @param {Object} data The JSON data loaded
4388      */
4389     /**
4390      * @event load
4391      * Fires when data is loaded.
4392      * @param {Roo.JsonView} this
4393      * @param {Object} data The JSON data loaded
4394      * @param {Object} response The raw Connect response object
4395      */
4396     /**
4397      * @event loadexception
4398      * Fires when loading fails.
4399      * @param {Roo.JsonView} this
4400      * @param {Object} response The raw Connect response object
4401      */
4402     this.addEvents({
4403         'beforerender' : true,
4404         'load' : true,
4405         'loadexception' : true
4406     });
4407 };
4408 Roo.extend(Roo.JsonView, Roo.View, {
4409     /**
4410      * @type {String} The root property in the loaded JSON object that contains the data
4411      */
4412     jsonRoot : "",
4413
4414     /**
4415      * Refreshes the view.
4416      */
4417     refresh : function(){
4418         this.clearSelections();
4419         this.el.update("");
4420         var html = [];
4421         var o = this.jsonData;
4422         if(o && o.length > 0){
4423             for(var i = 0, len = o.length; i < len; i++){
4424                 var data = this.prepareData(o[i], i, o);
4425                 html[html.length] = this.tpl.apply(data);
4426             }
4427         }else{
4428             html.push(this.emptyText);
4429         }
4430         this.el.update(html.join(""));
4431         this.nodes = this.el.dom.childNodes;
4432         this.updateIndexes(0);
4433     },
4434
4435     /**
4436      * 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.
4437      * @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:
4438      <pre><code>
4439      view.load({
4440          url: "your-url.php",
4441          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4442          callback: yourFunction,
4443          scope: yourObject, //(optional scope)
4444          discardUrl: false,
4445          nocache: false,
4446          text: "Loading...",
4447          timeout: 30,
4448          scripts: false
4449      });
4450      </code></pre>
4451      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4452      * 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.
4453      * @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}
4454      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4455      * @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.
4456      */
4457     load : function(){
4458         var um = this.el.getUpdateManager();
4459         um.update.apply(um, arguments);
4460     },
4461
4462     // note - render is a standard framework call...
4463     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4464     render : function(el, response){
4465         
4466         this.clearSelections();
4467         this.el.update("");
4468         var o;
4469         try{
4470             if (response != '') {
4471                 o = Roo.util.JSON.decode(response.responseText);
4472                 if(this.jsonRoot){
4473                     
4474                     o = o[this.jsonRoot];
4475                 }
4476             }
4477         } catch(e){
4478         }
4479         /**
4480          * The current JSON data or null
4481          */
4482         this.jsonData = o;
4483         this.beforeRender();
4484         this.refresh();
4485     },
4486
4487 /**
4488  * Get the number of records in the current JSON dataset
4489  * @return {Number}
4490  */
4491     getCount : function(){
4492         return this.jsonData ? this.jsonData.length : 0;
4493     },
4494
4495 /**
4496  * Returns the JSON object for the specified node(s)
4497  * @param {HTMLElement/Array} node The node or an array of nodes
4498  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4499  * you get the JSON object for the node
4500  */
4501     getNodeData : function(node){
4502         if(node instanceof Array){
4503             var data = [];
4504             for(var i = 0, len = node.length; i < len; i++){
4505                 data.push(this.getNodeData(node[i]));
4506             }
4507             return data;
4508         }
4509         return this.jsonData[this.indexOf(node)] || null;
4510     },
4511
4512     beforeRender : function(){
4513         this.snapshot = this.jsonData;
4514         if(this.sortInfo){
4515             this.sort.apply(this, this.sortInfo);
4516         }
4517         this.fireEvent("beforerender", this, this.jsonData);
4518     },
4519
4520     onLoad : function(el, o){
4521         this.fireEvent("load", this, this.jsonData, o);
4522     },
4523
4524     onLoadException : function(el, o){
4525         this.fireEvent("loadexception", this, o);
4526     },
4527
4528 /**
4529  * Filter the data by a specific property.
4530  * @param {String} property A property on your JSON objects
4531  * @param {String/RegExp} value Either string that the property values
4532  * should start with, or a RegExp to test against the property
4533  */
4534     filter : function(property, value){
4535         if(this.jsonData){
4536             var data = [];
4537             var ss = this.snapshot;
4538             if(typeof value == "string"){
4539                 var vlen = value.length;
4540                 if(vlen == 0){
4541                     this.clearFilter();
4542                     return;
4543                 }
4544                 value = value.toLowerCase();
4545                 for(var i = 0, len = ss.length; i < len; i++){
4546                     var o = ss[i];
4547                     if(o[property].substr(0, vlen).toLowerCase() == value){
4548                         data.push(o);
4549                     }
4550                 }
4551             } else if(value.exec){ // regex?
4552                 for(var i = 0, len = ss.length; i < len; i++){
4553                     var o = ss[i];
4554                     if(value.test(o[property])){
4555                         data.push(o);
4556                     }
4557                 }
4558             } else{
4559                 return;
4560             }
4561             this.jsonData = data;
4562             this.refresh();
4563         }
4564     },
4565
4566 /**
4567  * Filter by a function. The passed function will be called with each
4568  * object in the current dataset. If the function returns true the value is kept,
4569  * otherwise it is filtered.
4570  * @param {Function} fn
4571  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4572  */
4573     filterBy : function(fn, scope){
4574         if(this.jsonData){
4575             var data = [];
4576             var ss = this.snapshot;
4577             for(var i = 0, len = ss.length; i < len; i++){
4578                 var o = ss[i];
4579                 if(fn.call(scope || this, o)){
4580                     data.push(o);
4581                 }
4582             }
4583             this.jsonData = data;
4584             this.refresh();
4585         }
4586     },
4587
4588 /**
4589  * Clears the current filter.
4590  */
4591     clearFilter : function(){
4592         if(this.snapshot && this.jsonData != this.snapshot){
4593             this.jsonData = this.snapshot;
4594             this.refresh();
4595         }
4596     },
4597
4598
4599 /**
4600  * Sorts the data for this view and refreshes it.
4601  * @param {String} property A property on your JSON objects to sort on
4602  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4603  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4604  */
4605     sort : function(property, dir, sortType){
4606         this.sortInfo = Array.prototype.slice.call(arguments, 0);
4607         if(this.jsonData){
4608             var p = property;
4609             var dsc = dir && dir.toLowerCase() == "desc";
4610             var f = function(o1, o2){
4611                 var v1 = sortType ? sortType(o1[p]) : o1[p];
4612                 var v2 = sortType ? sortType(o2[p]) : o2[p];
4613                 ;
4614                 if(v1 < v2){
4615                     return dsc ? +1 : -1;
4616                 } else if(v1 > v2){
4617                     return dsc ? -1 : +1;
4618                 } else{
4619                     return 0;
4620                 }
4621             };
4622             this.jsonData.sort(f);
4623             this.refresh();
4624             if(this.jsonData != this.snapshot){
4625                 this.snapshot.sort(f);
4626             }
4627         }
4628     }
4629 });/*
4630  * Based on:
4631  * Ext JS Library 1.1.1
4632  * Copyright(c) 2006-2007, Ext JS, LLC.
4633  *
4634  * Originally Released Under LGPL - original licence link has changed is not relivant.
4635  *
4636  * Fork - LGPL
4637  * <script type="text/javascript">
4638  */
4639  
4640
4641 /**
4642  * @class Roo.ColorPalette
4643  * @extends Roo.Component
4644  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
4645  * Here's an example of typical usage:
4646  * <pre><code>
4647 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
4648 cp.render('my-div');
4649
4650 cp.on('select', function(palette, selColor){
4651     // do something with selColor
4652 });
4653 </code></pre>
4654  * @constructor
4655  * Create a new ColorPalette
4656  * @param {Object} config The config object
4657  */
4658 Roo.ColorPalette = function(config){
4659     Roo.ColorPalette.superclass.constructor.call(this, config);
4660     this.addEvents({
4661         /**
4662              * @event select
4663              * Fires when a color is selected
4664              * @param {ColorPalette} this
4665              * @param {String} color The 6-digit color hex code (without the # symbol)
4666              */
4667         select: true
4668     });
4669
4670     if(this.handler){
4671         this.on("select", this.handler, this.scope, true);
4672     }
4673 };
4674 Roo.extend(Roo.ColorPalette, Roo.Component, {
4675     /**
4676      * @cfg {String} itemCls
4677      * The CSS class to apply to the containing element (defaults to "x-color-palette")
4678      */
4679     itemCls : "x-color-palette",
4680     /**
4681      * @cfg {String} value
4682      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
4683      * the hex codes are case-sensitive.
4684      */
4685     value : null,
4686     clickEvent:'click',
4687     // private
4688     ctype: "Roo.ColorPalette",
4689
4690     /**
4691      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4692      */
4693     allowReselect : false,
4694
4695     /**
4696      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
4697      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
4698      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4699      * of colors with the width setting until the box is symmetrical.</p>
4700      * <p>You can override individual colors if needed:</p>
4701      * <pre><code>
4702 var cp = new Roo.ColorPalette();
4703 cp.colors[0] = "FF0000";  // change the first box to red
4704 </code></pre>
4705
4706 Or you can provide a custom array of your own for complete control:
4707 <pre><code>
4708 var cp = new Roo.ColorPalette();
4709 cp.colors = ["000000", "993300", "333300"];
4710 </code></pre>
4711      * @type Array
4712      */
4713     colors : [
4714         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4715         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4716         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4717         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4718         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4719     ],
4720
4721     // private
4722     onRender : function(container, position){
4723         var t = new Roo.MasterTemplate(
4724             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
4725         );
4726         var c = this.colors;
4727         for(var i = 0, len = c.length; i < len; i++){
4728             t.add([c[i]]);
4729         }
4730         var el = document.createElement("div");
4731         el.className = this.itemCls;
4732         t.overwrite(el);
4733         container.dom.insertBefore(el, position);
4734         this.el = Roo.get(el);
4735         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
4736         if(this.clickEvent != 'click'){
4737             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
4738         }
4739     },
4740
4741     // private
4742     afterRender : function(){
4743         Roo.ColorPalette.superclass.afterRender.call(this);
4744         if(this.value){
4745             var s = this.value;
4746             this.value = null;
4747             this.select(s);
4748         }
4749     },
4750
4751     // private
4752     handleClick : function(e, t){
4753         e.preventDefault();
4754         if(!this.disabled){
4755             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4756             this.select(c.toUpperCase());
4757         }
4758     },
4759
4760     /**
4761      * Selects the specified color in the palette (fires the select event)
4762      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4763      */
4764     select : function(color){
4765         color = color.replace("#", "");
4766         if(color != this.value || this.allowReselect){
4767             var el = this.el;
4768             if(this.value){
4769                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4770             }
4771             el.child("a.color-"+color).addClass("x-color-palette-sel");
4772             this.value = color;
4773             this.fireEvent("select", this, color);
4774         }
4775     }
4776 });/*
4777  * Based on:
4778  * Ext JS Library 1.1.1
4779  * Copyright(c) 2006-2007, Ext JS, LLC.
4780  *
4781  * Originally Released Under LGPL - original licence link has changed is not relivant.
4782  *
4783  * Fork - LGPL
4784  * <script type="text/javascript">
4785  */
4786  
4787 /**
4788  * @class Roo.DatePicker
4789  * @extends Roo.Component
4790  * Simple date picker class.
4791  * @constructor
4792  * Create a new DatePicker
4793  * @param {Object} config The config object
4794  */
4795 Roo.DatePicker = function(config){
4796     Roo.DatePicker.superclass.constructor.call(this, config);
4797
4798     this.value = config && config.value ?
4799                  config.value.clearTime() : new Date().clearTime();
4800
4801     this.addEvents({
4802         /**
4803              * @event select
4804              * Fires when a date is selected
4805              * @param {DatePicker} this
4806              * @param {Date} date The selected date
4807              */
4808         'select': true,
4809         /**
4810              * @event monthchange
4811              * Fires when the displayed month changes 
4812              * @param {DatePicker} this
4813              * @param {Date} date The selected month
4814              */
4815         'monthchange': true
4816     });
4817
4818     if(this.handler){
4819         this.on("select", this.handler,  this.scope || this);
4820     }
4821     // build the disabledDatesRE
4822     if(!this.disabledDatesRE && this.disabledDates){
4823         var dd = this.disabledDates;
4824         var re = "(?:";
4825         for(var i = 0; i < dd.length; i++){
4826             re += dd[i];
4827             if(i != dd.length-1) {
4828                 re += "|";
4829             }
4830         }
4831         this.disabledDatesRE = new RegExp(re + ")");
4832     }
4833 };
4834
4835 Roo.extend(Roo.DatePicker, Roo.Component, {
4836     /**
4837      * @cfg {String} todayText
4838      * The text to display on the button that selects the current date (defaults to "Today")
4839      */
4840     todayText : "Today",
4841     /**
4842      * @cfg {String} okText
4843      * The text to display on the ok button
4844      */
4845     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
4846     /**
4847      * @cfg {String} cancelText
4848      * The text to display on the cancel button
4849      */
4850     cancelText : "Cancel",
4851     /**
4852      * @cfg {String} todayTip
4853      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4854      */
4855     todayTip : "{0} (Spacebar)",
4856     /**
4857      * @cfg {Date} minDate
4858      * Minimum allowable date (JavaScript date object, defaults to null)
4859      */
4860     minDate : null,
4861     /**
4862      * @cfg {Date} maxDate
4863      * Maximum allowable date (JavaScript date object, defaults to null)
4864      */
4865     maxDate : null,
4866     /**
4867      * @cfg {String} minText
4868      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4869      */
4870     minText : "This date is before the minimum date",
4871     /**
4872      * @cfg {String} maxText
4873      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4874      */
4875     maxText : "This date is after the maximum date",
4876     /**
4877      * @cfg {String} format
4878      * The default date format string which can be overriden for localization support.  The format must be
4879      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4880      */
4881     format : "m/d/y",
4882     /**
4883      * @cfg {Array} disabledDays
4884      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4885      */
4886     disabledDays : null,
4887     /**
4888      * @cfg {String} disabledDaysText
4889      * The tooltip to display when the date falls on a disabled day (defaults to "")
4890      */
4891     disabledDaysText : "",
4892     /**
4893      * @cfg {RegExp} disabledDatesRE
4894      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4895      */
4896     disabledDatesRE : null,
4897     /**
4898      * @cfg {String} disabledDatesText
4899      * The tooltip text to display when the date falls on a disabled date (defaults to "")
4900      */
4901     disabledDatesText : "",
4902     /**
4903      * @cfg {Boolean} constrainToViewport
4904      * True to constrain the date picker to the viewport (defaults to true)
4905      */
4906     constrainToViewport : true,
4907     /**
4908      * @cfg {Array} monthNames
4909      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4910      */
4911     monthNames : Date.monthNames,
4912     /**
4913      * @cfg {Array} dayNames
4914      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4915      */
4916     dayNames : Date.dayNames,
4917     /**
4918      * @cfg {String} nextText
4919      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4920      */
4921     nextText: 'Next Month (Control+Right)',
4922     /**
4923      * @cfg {String} prevText
4924      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4925      */
4926     prevText: 'Previous Month (Control+Left)',
4927     /**
4928      * @cfg {String} monthYearText
4929      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4930      */
4931     monthYearText: 'Choose a month (Control+Up/Down to move years)',
4932     /**
4933      * @cfg {Number} startDay
4934      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4935      */
4936     startDay : 0,
4937     /**
4938      * @cfg {Bool} showClear
4939      * Show a clear button (usefull for date form elements that can be blank.)
4940      */
4941     
4942     showClear: false,
4943     
4944     /**
4945      * Sets the value of the date field
4946      * @param {Date} value The date to set
4947      */
4948     setValue : function(value){
4949         var old = this.value;
4950         
4951         if (typeof(value) == 'string') {
4952          
4953             value = Date.parseDate(value, this.format);
4954         }
4955         if (!value) {
4956             value = new Date();
4957         }
4958         
4959         this.value = value.clearTime(true);
4960         if(this.el){
4961             this.update(this.value);
4962         }
4963     },
4964
4965     /**
4966      * Gets the current selected value of the date field
4967      * @return {Date} The selected date
4968      */
4969     getValue : function(){
4970         return this.value;
4971     },
4972
4973     // private
4974     focus : function(){
4975         if(this.el){
4976             this.update(this.activeDate);
4977         }
4978     },
4979
4980     // privateval
4981     onRender : function(container, position){
4982         
4983         var m = [
4984              '<table cellspacing="0">',
4985                 '<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>',
4986                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
4987         var dn = this.dayNames;
4988         for(var i = 0; i < 7; i++){
4989             var d = this.startDay+i;
4990             if(d > 6){
4991                 d = d-7;
4992             }
4993             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
4994         }
4995         m[m.length] = "</tr></thead><tbody><tr>";
4996         for(var i = 0; i < 42; i++) {
4997             if(i % 7 == 0 && i != 0){
4998                 m[m.length] = "</tr><tr>";
4999             }
5000             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5001         }
5002         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5003             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5004
5005         var el = document.createElement("div");
5006         el.className = "x-date-picker";
5007         el.innerHTML = m.join("");
5008
5009         container.dom.insertBefore(el, position);
5010
5011         this.el = Roo.get(el);
5012         this.eventEl = Roo.get(el.firstChild);
5013
5014         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5015             handler: this.showPrevMonth,
5016             scope: this,
5017             preventDefault:true,
5018             stopDefault:true
5019         });
5020
5021         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5022             handler: this.showNextMonth,
5023             scope: this,
5024             preventDefault:true,
5025             stopDefault:true
5026         });
5027
5028         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5029
5030         this.monthPicker = this.el.down('div.x-date-mp');
5031         this.monthPicker.enableDisplayMode('block');
5032         
5033         var kn = new Roo.KeyNav(this.eventEl, {
5034             "left" : function(e){
5035                 e.ctrlKey ?
5036                     this.showPrevMonth() :
5037                     this.update(this.activeDate.add("d", -1));
5038             },
5039
5040             "right" : function(e){
5041                 e.ctrlKey ?
5042                     this.showNextMonth() :
5043                     this.update(this.activeDate.add("d", 1));
5044             },
5045
5046             "up" : function(e){
5047                 e.ctrlKey ?
5048                     this.showNextYear() :
5049                     this.update(this.activeDate.add("d", -7));
5050             },
5051
5052             "down" : function(e){
5053                 e.ctrlKey ?
5054                     this.showPrevYear() :
5055                     this.update(this.activeDate.add("d", 7));
5056             },
5057
5058             "pageUp" : function(e){
5059                 this.showNextMonth();
5060             },
5061
5062             "pageDown" : function(e){
5063                 this.showPrevMonth();
5064             },
5065
5066             "enter" : function(e){
5067                 e.stopPropagation();
5068                 return true;
5069             },
5070
5071             scope : this
5072         });
5073
5074         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5075
5076         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5077
5078         this.el.unselectable();
5079         
5080         this.cells = this.el.select("table.x-date-inner tbody td");
5081         this.textNodes = this.el.query("table.x-date-inner tbody span");
5082
5083         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5084             text: "&#160;",
5085             tooltip: this.monthYearText
5086         });
5087
5088         this.mbtn.on('click', this.showMonthPicker, this);
5089         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5090
5091
5092         var today = (new Date()).dateFormat(this.format);
5093         
5094         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5095         if (this.showClear) {
5096             baseTb.add( new Roo.Toolbar.Fill());
5097         }
5098         baseTb.add({
5099             text: String.format(this.todayText, today),
5100             tooltip: String.format(this.todayTip, today),
5101             handler: this.selectToday,
5102             scope: this
5103         });
5104         
5105         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5106             
5107         //});
5108         if (this.showClear) {
5109             
5110             baseTb.add( new Roo.Toolbar.Fill());
5111             baseTb.add({
5112                 text: '&#160;',
5113                 cls: 'x-btn-icon x-btn-clear',
5114                 handler: function() {
5115                     //this.value = '';
5116                     this.fireEvent("select", this, '');
5117                 },
5118                 scope: this
5119             });
5120         }
5121         
5122         
5123         if(Roo.isIE){
5124             this.el.repaint();
5125         }
5126         this.update(this.value);
5127     },
5128
5129     createMonthPicker : function(){
5130         if(!this.monthPicker.dom.firstChild){
5131             var buf = ['<table border="0" cellspacing="0">'];
5132             for(var i = 0; i < 6; i++){
5133                 buf.push(
5134                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5135                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5136                     i == 0 ?
5137                     '<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>' :
5138                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5139                 );
5140             }
5141             buf.push(
5142                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5143                     this.okText,
5144                     '</button><button type="button" class="x-date-mp-cancel">',
5145                     this.cancelText,
5146                     '</button></td></tr>',
5147                 '</table>'
5148             );
5149             this.monthPicker.update(buf.join(''));
5150             this.monthPicker.on('click', this.onMonthClick, this);
5151             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5152
5153             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5154             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5155
5156             this.mpMonths.each(function(m, a, i){
5157                 i += 1;
5158                 if((i%2) == 0){
5159                     m.dom.xmonth = 5 + Math.round(i * .5);
5160                 }else{
5161                     m.dom.xmonth = Math.round((i-1) * .5);
5162                 }
5163             });
5164         }
5165     },
5166
5167     showMonthPicker : function(){
5168         this.createMonthPicker();
5169         var size = this.el.getSize();
5170         this.monthPicker.setSize(size);
5171         this.monthPicker.child('table').setSize(size);
5172
5173         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5174         this.updateMPMonth(this.mpSelMonth);
5175         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5176         this.updateMPYear(this.mpSelYear);
5177
5178         this.monthPicker.slideIn('t', {duration:.2});
5179     },
5180
5181     updateMPYear : function(y){
5182         this.mpyear = y;
5183         var ys = this.mpYears.elements;
5184         for(var i = 1; i <= 10; i++){
5185             var td = ys[i-1], y2;
5186             if((i%2) == 0){
5187                 y2 = y + Math.round(i * .5);
5188                 td.firstChild.innerHTML = y2;
5189                 td.xyear = y2;
5190             }else{
5191                 y2 = y - (5-Math.round(i * .5));
5192                 td.firstChild.innerHTML = y2;
5193                 td.xyear = y2;
5194             }
5195             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5196         }
5197     },
5198
5199     updateMPMonth : function(sm){
5200         this.mpMonths.each(function(m, a, i){
5201             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5202         });
5203     },
5204
5205     selectMPMonth: function(m){
5206         
5207     },
5208
5209     onMonthClick : function(e, t){
5210         e.stopEvent();
5211         var el = new Roo.Element(t), pn;
5212         if(el.is('button.x-date-mp-cancel')){
5213             this.hideMonthPicker();
5214         }
5215         else if(el.is('button.x-date-mp-ok')){
5216             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5217             this.hideMonthPicker();
5218         }
5219         else if(pn = el.up('td.x-date-mp-month', 2)){
5220             this.mpMonths.removeClass('x-date-mp-sel');
5221             pn.addClass('x-date-mp-sel');
5222             this.mpSelMonth = pn.dom.xmonth;
5223         }
5224         else if(pn = el.up('td.x-date-mp-year', 2)){
5225             this.mpYears.removeClass('x-date-mp-sel');
5226             pn.addClass('x-date-mp-sel');
5227             this.mpSelYear = pn.dom.xyear;
5228         }
5229         else if(el.is('a.x-date-mp-prev')){
5230             this.updateMPYear(this.mpyear-10);
5231         }
5232         else if(el.is('a.x-date-mp-next')){
5233             this.updateMPYear(this.mpyear+10);
5234         }
5235     },
5236
5237     onMonthDblClick : function(e, t){
5238         e.stopEvent();
5239         var el = new Roo.Element(t), pn;
5240         if(pn = el.up('td.x-date-mp-month', 2)){
5241             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5242             this.hideMonthPicker();
5243         }
5244         else if(pn = el.up('td.x-date-mp-year', 2)){
5245             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5246             this.hideMonthPicker();
5247         }
5248     },
5249
5250     hideMonthPicker : function(disableAnim){
5251         if(this.monthPicker){
5252             if(disableAnim === true){
5253                 this.monthPicker.hide();
5254             }else{
5255                 this.monthPicker.slideOut('t', {duration:.2});
5256             }
5257         }
5258     },
5259
5260     // private
5261     showPrevMonth : function(e){
5262         this.update(this.activeDate.add("mo", -1));
5263     },
5264
5265     // private
5266     showNextMonth : function(e){
5267         this.update(this.activeDate.add("mo", 1));
5268     },
5269
5270     // private
5271     showPrevYear : function(){
5272         this.update(this.activeDate.add("y", -1));
5273     },
5274
5275     // private
5276     showNextYear : function(){
5277         this.update(this.activeDate.add("y", 1));
5278     },
5279
5280     // private
5281     handleMouseWheel : function(e){
5282         var delta = e.getWheelDelta();
5283         if(delta > 0){
5284             this.showPrevMonth();
5285             e.stopEvent();
5286         } else if(delta < 0){
5287             this.showNextMonth();
5288             e.stopEvent();
5289         }
5290     },
5291
5292     // private
5293     handleDateClick : function(e, t){
5294         e.stopEvent();
5295         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5296             this.setValue(new Date(t.dateValue));
5297             this.fireEvent("select", this, this.value);
5298         }
5299     },
5300
5301     // private
5302     selectToday : function(){
5303         this.setValue(new Date().clearTime());
5304         this.fireEvent("select", this, this.value);
5305     },
5306
5307     // private
5308     update : function(date)
5309     {
5310         var vd = this.activeDate;
5311         this.activeDate = date;
5312         if(vd && this.el){
5313             var t = date.getTime();
5314             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5315                 this.cells.removeClass("x-date-selected");
5316                 this.cells.each(function(c){
5317                    if(c.dom.firstChild.dateValue == t){
5318                        c.addClass("x-date-selected");
5319                        setTimeout(function(){
5320                             try{c.dom.firstChild.focus();}catch(e){}
5321                        }, 50);
5322                        return false;
5323                    }
5324                 });
5325                 return;
5326             }
5327         }
5328         
5329         var days = date.getDaysInMonth();
5330         var firstOfMonth = date.getFirstDateOfMonth();
5331         var startingPos = firstOfMonth.getDay()-this.startDay;
5332
5333         if(startingPos <= this.startDay){
5334             startingPos += 7;
5335         }
5336
5337         var pm = date.add("mo", -1);
5338         var prevStart = pm.getDaysInMonth()-startingPos;
5339
5340         var cells = this.cells.elements;
5341         var textEls = this.textNodes;
5342         days += startingPos;
5343
5344         // convert everything to numbers so it's fast
5345         var day = 86400000;
5346         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5347         var today = new Date().clearTime().getTime();
5348         var sel = date.clearTime().getTime();
5349         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5350         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5351         var ddMatch = this.disabledDatesRE;
5352         var ddText = this.disabledDatesText;
5353         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5354         var ddaysText = this.disabledDaysText;
5355         var format = this.format;
5356
5357         var setCellClass = function(cal, cell){
5358             cell.title = "";
5359             var t = d.getTime();
5360             cell.firstChild.dateValue = t;
5361             if(t == today){
5362                 cell.className += " x-date-today";
5363                 cell.title = cal.todayText;
5364             }
5365             if(t == sel){
5366                 cell.className += " x-date-selected";
5367                 setTimeout(function(){
5368                     try{cell.firstChild.focus();}catch(e){}
5369                 }, 50);
5370             }
5371             // disabling
5372             if(t < min) {
5373                 cell.className = " x-date-disabled";
5374                 cell.title = cal.minText;
5375                 return;
5376             }
5377             if(t > max) {
5378                 cell.className = " x-date-disabled";
5379                 cell.title = cal.maxText;
5380                 return;
5381             }
5382             if(ddays){
5383                 if(ddays.indexOf(d.getDay()) != -1){
5384                     cell.title = ddaysText;
5385                     cell.className = " x-date-disabled";
5386                 }
5387             }
5388             if(ddMatch && format){
5389                 var fvalue = d.dateFormat(format);
5390                 if(ddMatch.test(fvalue)){
5391                     cell.title = ddText.replace("%0", fvalue);
5392                     cell.className = " x-date-disabled";
5393                 }
5394             }
5395         };
5396
5397         var i = 0;
5398         for(; i < startingPos; i++) {
5399             textEls[i].innerHTML = (++prevStart);
5400             d.setDate(d.getDate()+1);
5401             cells[i].className = "x-date-prevday";
5402             setCellClass(this, cells[i]);
5403         }
5404         for(; i < days; i++){
5405             intDay = i - startingPos + 1;
5406             textEls[i].innerHTML = (intDay);
5407             d.setDate(d.getDate()+1);
5408             cells[i].className = "x-date-active";
5409             setCellClass(this, cells[i]);
5410         }
5411         var extraDays = 0;
5412         for(; i < 42; i++) {
5413              textEls[i].innerHTML = (++extraDays);
5414              d.setDate(d.getDate()+1);
5415              cells[i].className = "x-date-nextday";
5416              setCellClass(this, cells[i]);
5417         }
5418
5419         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5420         this.fireEvent('monthchange', this, date);
5421         
5422         if(!this.internalRender){
5423             var main = this.el.dom.firstChild;
5424             var w = main.offsetWidth;
5425             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5426             Roo.fly(main).setWidth(w);
5427             this.internalRender = true;
5428             // opera does not respect the auto grow header center column
5429             // then, after it gets a width opera refuses to recalculate
5430             // without a second pass
5431             if(Roo.isOpera && !this.secondPass){
5432                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5433                 this.secondPass = true;
5434                 this.update.defer(10, this, [date]);
5435             }
5436         }
5437         
5438         
5439     }
5440 });        /*
5441  * Based on:
5442  * Ext JS Library 1.1.1
5443  * Copyright(c) 2006-2007, Ext JS, LLC.
5444  *
5445  * Originally Released Under LGPL - original licence link has changed is not relivant.
5446  *
5447  * Fork - LGPL
5448  * <script type="text/javascript">
5449  */
5450 /**
5451  * @class Roo.TabPanel
5452  * @extends Roo.util.Observable
5453  * A lightweight tab container.
5454  * <br><br>
5455  * Usage:
5456  * <pre><code>
5457 // basic tabs 1, built from existing content
5458 var tabs = new Roo.TabPanel("tabs1");
5459 tabs.addTab("script", "View Script");
5460 tabs.addTab("markup", "View Markup");
5461 tabs.activate("script");
5462
5463 // more advanced tabs, built from javascript
5464 var jtabs = new Roo.TabPanel("jtabs");
5465 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5466
5467 // set up the UpdateManager
5468 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5469 var updater = tab2.getUpdateManager();
5470 updater.setDefaultUrl("ajax1.htm");
5471 tab2.on('activate', updater.refresh, updater, true);
5472
5473 // Use setUrl for Ajax loading
5474 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5475 tab3.setUrl("ajax2.htm", null, true);
5476
5477 // Disabled tab
5478 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5479 tab4.disable();
5480
5481 jtabs.activate("jtabs-1");
5482  * </code></pre>
5483  * @constructor
5484  * Create a new TabPanel.
5485  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5486  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5487  */
5488 Roo.TabPanel = function(container, config){
5489     /**
5490     * The container element for this TabPanel.
5491     * @type Roo.Element
5492     */
5493     this.el = Roo.get(container, true);
5494     if(config){
5495         if(typeof config == "boolean"){
5496             this.tabPosition = config ? "bottom" : "top";
5497         }else{
5498             Roo.apply(this, config);
5499         }
5500     }
5501     if(this.tabPosition == "bottom"){
5502         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5503         this.el.addClass("x-tabs-bottom");
5504     }
5505     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5506     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5507     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5508     if(Roo.isIE){
5509         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5510     }
5511     if(this.tabPosition != "bottom"){
5512         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5513          * @type Roo.Element
5514          */
5515         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5516         this.el.addClass("x-tabs-top");
5517     }
5518     this.items = [];
5519
5520     this.bodyEl.setStyle("position", "relative");
5521
5522     this.active = null;
5523     this.activateDelegate = this.activate.createDelegate(this);
5524
5525     this.addEvents({
5526         /**
5527          * @event tabchange
5528          * Fires when the active tab changes
5529          * @param {Roo.TabPanel} this
5530          * @param {Roo.TabPanelItem} activePanel The new active tab
5531          */
5532         "tabchange": true,
5533         /**
5534          * @event beforetabchange
5535          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5536          * @param {Roo.TabPanel} this
5537          * @param {Object} e Set cancel to true on this object to cancel the tab change
5538          * @param {Roo.TabPanelItem} tab The tab being changed to
5539          */
5540         "beforetabchange" : true
5541     });
5542
5543     Roo.EventManager.onWindowResize(this.onResize, this);
5544     this.cpad = this.el.getPadding("lr");
5545     this.hiddenCount = 0;
5546
5547
5548     // toolbar on the tabbar support...
5549     if (this.toolbar) {
5550         var tcfg = this.toolbar;
5551         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5552         this.toolbar = new Roo.Toolbar(tcfg);
5553         if (Roo.isSafari) {
5554             var tbl = tcfg.container.child('table', true);
5555             tbl.setAttribute('width', '100%');
5556         }
5557         
5558     }
5559    
5560
5561
5562     Roo.TabPanel.superclass.constructor.call(this);
5563 };
5564
5565 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5566     /*
5567      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5568      */
5569     tabPosition : "top",
5570     /*
5571      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5572      */
5573     currentTabWidth : 0,
5574     /*
5575      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5576      */
5577     minTabWidth : 40,
5578     /*
5579      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5580      */
5581     maxTabWidth : 250,
5582     /*
5583      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5584      */
5585     preferredTabWidth : 175,
5586     /*
5587      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5588      */
5589     resizeTabs : false,
5590     /*
5591      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5592      */
5593     monitorResize : true,
5594     /*
5595      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
5596      */
5597     toolbar : false,
5598
5599     /**
5600      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5601      * @param {String} id The id of the div to use <b>or create</b>
5602      * @param {String} text The text for the tab
5603      * @param {String} content (optional) Content to put in the TabPanelItem body
5604      * @param {Boolean} closable (optional) True to create a close icon on the tab
5605      * @return {Roo.TabPanelItem} The created TabPanelItem
5606      */
5607     addTab : function(id, text, content, closable){
5608         var item = new Roo.TabPanelItem(this, id, text, closable);
5609         this.addTabItem(item);
5610         if(content){
5611             item.setContent(content);
5612         }
5613         return item;
5614     },
5615
5616     /**
5617      * Returns the {@link Roo.TabPanelItem} with the specified id/index
5618      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5619      * @return {Roo.TabPanelItem}
5620      */
5621     getTab : function(id){
5622         return this.items[id];
5623     },
5624
5625     /**
5626      * Hides the {@link Roo.TabPanelItem} with the specified id/index
5627      * @param {String/Number} id The id or index of the TabPanelItem to hide.
5628      */
5629     hideTab : function(id){
5630         var t = this.items[id];
5631         if(!t.isHidden()){
5632            t.setHidden(true);
5633            this.hiddenCount++;
5634            this.autoSizeTabs();
5635         }
5636     },
5637
5638     /**
5639      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5640      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5641      */
5642     unhideTab : function(id){
5643         var t = this.items[id];
5644         if(t.isHidden()){
5645            t.setHidden(false);
5646            this.hiddenCount--;
5647            this.autoSizeTabs();
5648         }
5649     },
5650
5651     /**
5652      * Adds an existing {@link Roo.TabPanelItem}.
5653      * @param {Roo.TabPanelItem} item The TabPanelItem to add
5654      */
5655     addTabItem : function(item){
5656         this.items[item.id] = item;
5657         this.items.push(item);
5658         if(this.resizeTabs){
5659            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5660            this.autoSizeTabs();
5661         }else{
5662             item.autoSize();
5663         }
5664     },
5665
5666     /**
5667      * Removes a {@link Roo.TabPanelItem}.
5668      * @param {String/Number} id The id or index of the TabPanelItem to remove.
5669      */
5670     removeTab : function(id){
5671         var items = this.items;
5672         var tab = items[id];
5673         if(!tab) { return; }
5674         var index = items.indexOf(tab);
5675         if(this.active == tab && items.length > 1){
5676             var newTab = this.getNextAvailable(index);
5677             if(newTab) {
5678                 newTab.activate();
5679             }
5680         }
5681         this.stripEl.dom.removeChild(tab.pnode.dom);
5682         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5683             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5684         }
5685         items.splice(index, 1);
5686         delete this.items[tab.id];
5687         tab.fireEvent("close", tab);
5688         tab.purgeListeners();
5689         this.autoSizeTabs();
5690     },
5691
5692     getNextAvailable : function(start){
5693         var items = this.items;
5694         var index = start;
5695         // look for a next tab that will slide over to
5696         // replace the one being removed
5697         while(index < items.length){
5698             var item = items[++index];
5699             if(item && !item.isHidden()){
5700                 return item;
5701             }
5702         }
5703         // if one isn't found select the previous tab (on the left)
5704         index = start;
5705         while(index >= 0){
5706             var item = items[--index];
5707             if(item && !item.isHidden()){
5708                 return item;
5709             }
5710         }
5711         return null;
5712     },
5713
5714     /**
5715      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5716      * @param {String/Number} id The id or index of the TabPanelItem to disable.
5717      */
5718     disableTab : function(id){
5719         var tab = this.items[id];
5720         if(tab && this.active != tab){
5721             tab.disable();
5722         }
5723     },
5724
5725     /**
5726      * Enables a {@link Roo.TabPanelItem} that is disabled.
5727      * @param {String/Number} id The id or index of the TabPanelItem to enable.
5728      */
5729     enableTab : function(id){
5730         var tab = this.items[id];
5731         tab.enable();
5732     },
5733
5734     /**
5735      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5736      * @param {String/Number} id The id or index of the TabPanelItem to activate.
5737      * @return {Roo.TabPanelItem} The TabPanelItem.
5738      */
5739     activate : function(id){
5740         var tab = this.items[id];
5741         if(!tab){
5742             return null;
5743         }
5744         if(tab == this.active || tab.disabled){
5745             return tab;
5746         }
5747         var e = {};
5748         this.fireEvent("beforetabchange", this, e, tab);
5749         if(e.cancel !== true && !tab.disabled){
5750             if(this.active){
5751                 this.active.hide();
5752             }
5753             this.active = this.items[id];
5754             this.active.show();
5755             this.fireEvent("tabchange", this, this.active);
5756         }
5757         return tab;
5758     },
5759
5760     /**
5761      * Gets the active {@link Roo.TabPanelItem}.
5762      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5763      */
5764     getActiveTab : function(){
5765         return this.active;
5766     },
5767
5768     /**
5769      * Updates the tab body element to fit the height of the container element
5770      * for overflow scrolling
5771      * @param {Number} targetHeight (optional) Override the starting height from the elements height
5772      */
5773     syncHeight : function(targetHeight){
5774         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5775         var bm = this.bodyEl.getMargins();
5776         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5777         this.bodyEl.setHeight(newHeight);
5778         return newHeight;
5779     },
5780
5781     onResize : function(){
5782         if(this.monitorResize){
5783             this.autoSizeTabs();
5784         }
5785     },
5786
5787     /**
5788      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5789      */
5790     beginUpdate : function(){
5791         this.updating = true;
5792     },
5793
5794     /**
5795      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5796      */
5797     endUpdate : function(){
5798         this.updating = false;
5799         this.autoSizeTabs();
5800     },
5801
5802     /**
5803      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5804      */
5805     autoSizeTabs : function(){
5806         var count = this.items.length;
5807         var vcount = count - this.hiddenCount;
5808         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5809             return;
5810         }
5811         var w = Math.max(this.el.getWidth() - this.cpad, 10);
5812         var availWidth = Math.floor(w / vcount);
5813         var b = this.stripBody;
5814         if(b.getWidth() > w){
5815             var tabs = this.items;
5816             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5817             if(availWidth < this.minTabWidth){
5818                 /*if(!this.sleft){    // incomplete scrolling code
5819                     this.createScrollButtons();
5820                 }
5821                 this.showScroll();
5822                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5823             }
5824         }else{
5825             if(this.currentTabWidth < this.preferredTabWidth){
5826                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5827             }
5828         }
5829     },
5830
5831     /**
5832      * Returns the number of tabs in this TabPanel.
5833      * @return {Number}
5834      */
5835      getCount : function(){
5836          return this.items.length;
5837      },
5838
5839     /**
5840      * Resizes all the tabs to the passed width
5841      * @param {Number} The new width
5842      */
5843     setTabWidth : function(width){
5844         this.currentTabWidth = width;
5845         for(var i = 0, len = this.items.length; i < len; i++) {
5846                 if(!this.items[i].isHidden()) {
5847                 this.items[i].setWidth(width);
5848             }
5849         }
5850     },
5851
5852     /**
5853      * Destroys this TabPanel
5854      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5855      */
5856     destroy : function(removeEl){
5857         Roo.EventManager.removeResizeListener(this.onResize, this);
5858         for(var i = 0, len = this.items.length; i < len; i++){
5859             this.items[i].purgeListeners();
5860         }
5861         if(removeEl === true){
5862             this.el.update("");
5863             this.el.remove();
5864         }
5865     }
5866 });
5867
5868 /**
5869  * @class Roo.TabPanelItem
5870  * @extends Roo.util.Observable
5871  * Represents an individual item (tab plus body) in a TabPanel.
5872  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5873  * @param {String} id The id of this TabPanelItem
5874  * @param {String} text The text for the tab of this TabPanelItem
5875  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5876  */
5877 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5878     /**
5879      * The {@link Roo.TabPanel} this TabPanelItem belongs to
5880      * @type Roo.TabPanel
5881      */
5882     this.tabPanel = tabPanel;
5883     /**
5884      * The id for this TabPanelItem
5885      * @type String
5886      */
5887     this.id = id;
5888     /** @private */
5889     this.disabled = false;
5890     /** @private */
5891     this.text = text;
5892     /** @private */
5893     this.loaded = false;
5894     this.closable = closable;
5895
5896     /**
5897      * The body element for this TabPanelItem.
5898      * @type Roo.Element
5899      */
5900     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5901     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5902     this.bodyEl.setStyle("display", "block");
5903     this.bodyEl.setStyle("zoom", "1");
5904     this.hideAction();
5905
5906     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5907     /** @private */
5908     this.el = Roo.get(els.el, true);
5909     this.inner = Roo.get(els.inner, true);
5910     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5911     this.pnode = Roo.get(els.el.parentNode, true);
5912     this.el.on("mousedown", this.onTabMouseDown, this);
5913     this.el.on("click", this.onTabClick, this);
5914     /** @private */
5915     if(closable){
5916         var c = Roo.get(els.close, true);
5917         c.dom.title = this.closeText;
5918         c.addClassOnOver("close-over");
5919         c.on("click", this.closeClick, this);
5920      }
5921
5922     this.addEvents({
5923          /**
5924          * @event activate
5925          * Fires when this tab becomes the active tab.
5926          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5927          * @param {Roo.TabPanelItem} this
5928          */
5929         "activate": true,
5930         /**
5931          * @event beforeclose
5932          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5933          * @param {Roo.TabPanelItem} this
5934          * @param {Object} e Set cancel to true on this object to cancel the close.
5935          */
5936         "beforeclose": true,
5937         /**
5938          * @event close
5939          * Fires when this tab is closed.
5940          * @param {Roo.TabPanelItem} this
5941          */
5942          "close": true,
5943         /**
5944          * @event deactivate
5945          * Fires when this tab is no longer the active tab.
5946          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5947          * @param {Roo.TabPanelItem} this
5948          */
5949          "deactivate" : true
5950     });
5951     this.hidden = false;
5952
5953     Roo.TabPanelItem.superclass.constructor.call(this);
5954 };
5955
5956 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5957     purgeListeners : function(){
5958        Roo.util.Observable.prototype.purgeListeners.call(this);
5959        this.el.removeAllListeners();
5960     },
5961     /**
5962      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5963      */
5964     show : function(){
5965         this.pnode.addClass("on");
5966         this.showAction();
5967         if(Roo.isOpera){
5968             this.tabPanel.stripWrap.repaint();
5969         }
5970         this.fireEvent("activate", this.tabPanel, this);
5971     },
5972
5973     /**
5974      * Returns true if this tab is the active tab.
5975      * @return {Boolean}
5976      */
5977     isActive : function(){
5978         return this.tabPanel.getActiveTab() == this;
5979     },
5980
5981     /**
5982      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
5983      */
5984     hide : function(){
5985         this.pnode.removeClass("on");
5986         this.hideAction();
5987         this.fireEvent("deactivate", this.tabPanel, this);
5988     },
5989
5990     hideAction : function(){
5991         this.bodyEl.hide();
5992         this.bodyEl.setStyle("position", "absolute");
5993         this.bodyEl.setLeft("-20000px");
5994         this.bodyEl.setTop("-20000px");
5995     },
5996
5997     showAction : function(){
5998         this.bodyEl.setStyle("position", "relative");
5999         this.bodyEl.setTop("");
6000         this.bodyEl.setLeft("");
6001         this.bodyEl.show();
6002     },
6003
6004     /**
6005      * Set the tooltip for the tab.
6006      * @param {String} tooltip The tab's tooltip
6007      */
6008     setTooltip : function(text){
6009         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6010             this.textEl.dom.qtip = text;
6011             this.textEl.dom.removeAttribute('title');
6012         }else{
6013             this.textEl.dom.title = text;
6014         }
6015     },
6016
6017     onTabClick : function(e){
6018         e.preventDefault();
6019         this.tabPanel.activate(this.id);
6020     },
6021
6022     onTabMouseDown : function(e){
6023         e.preventDefault();
6024         this.tabPanel.activate(this.id);
6025     },
6026
6027     getWidth : function(){
6028         return this.inner.getWidth();
6029     },
6030
6031     setWidth : function(width){
6032         var iwidth = width - this.pnode.getPadding("lr");
6033         this.inner.setWidth(iwidth);
6034         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6035         this.pnode.setWidth(width);
6036     },
6037
6038     /**
6039      * Show or hide the tab
6040      * @param {Boolean} hidden True to hide or false to show.
6041      */
6042     setHidden : function(hidden){
6043         this.hidden = hidden;
6044         this.pnode.setStyle("display", hidden ? "none" : "");
6045     },
6046
6047     /**
6048      * Returns true if this tab is "hidden"
6049      * @return {Boolean}
6050      */
6051     isHidden : function(){
6052         return this.hidden;
6053     },
6054
6055     /**
6056      * Returns the text for this tab
6057      * @return {String}
6058      */
6059     getText : function(){
6060         return this.text;
6061     },
6062
6063     autoSize : function(){
6064         //this.el.beginMeasure();
6065         this.textEl.setWidth(1);
6066         /*
6067          *  #2804 [new] Tabs in Roojs
6068          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6069          */
6070         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6071         //this.el.endMeasure();
6072     },
6073
6074     /**
6075      * Sets the text for the tab (Note: this also sets the tooltip text)
6076      * @param {String} text The tab's text and tooltip
6077      */
6078     setText : function(text){
6079         this.text = text;
6080         this.textEl.update(text);
6081         this.setTooltip(text);
6082         if(!this.tabPanel.resizeTabs){
6083             this.autoSize();
6084         }
6085     },
6086     /**
6087      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6088      */
6089     activate : function(){
6090         this.tabPanel.activate(this.id);
6091     },
6092
6093     /**
6094      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6095      */
6096     disable : function(){
6097         if(this.tabPanel.active != this){
6098             this.disabled = true;
6099             this.pnode.addClass("disabled");
6100         }
6101     },
6102
6103     /**
6104      * Enables this TabPanelItem if it was previously disabled.
6105      */
6106     enable : function(){
6107         this.disabled = false;
6108         this.pnode.removeClass("disabled");
6109     },
6110
6111     /**
6112      * Sets the content for this TabPanelItem.
6113      * @param {String} content The content
6114      * @param {Boolean} loadScripts true to look for and load scripts
6115      */
6116     setContent : function(content, loadScripts){
6117         this.bodyEl.update(content, loadScripts);
6118     },
6119
6120     /**
6121      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6122      * @return {Roo.UpdateManager} The UpdateManager
6123      */
6124     getUpdateManager : function(){
6125         return this.bodyEl.getUpdateManager();
6126     },
6127
6128     /**
6129      * Set a URL to be used to load the content for this TabPanelItem.
6130      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6131      * @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)
6132      * @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)
6133      * @return {Roo.UpdateManager} The UpdateManager
6134      */
6135     setUrl : function(url, params, loadOnce){
6136         if(this.refreshDelegate){
6137             this.un('activate', this.refreshDelegate);
6138         }
6139         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6140         this.on("activate", this.refreshDelegate);
6141         return this.bodyEl.getUpdateManager();
6142     },
6143
6144     /** @private */
6145     _handleRefresh : function(url, params, loadOnce){
6146         if(!loadOnce || !this.loaded){
6147             var updater = this.bodyEl.getUpdateManager();
6148             updater.update(url, params, this._setLoaded.createDelegate(this));
6149         }
6150     },
6151
6152     /**
6153      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6154      *   Will fail silently if the setUrl method has not been called.
6155      *   This does not activate the panel, just updates its content.
6156      */
6157     refresh : function(){
6158         if(this.refreshDelegate){
6159            this.loaded = false;
6160            this.refreshDelegate();
6161         }
6162     },
6163
6164     /** @private */
6165     _setLoaded : function(){
6166         this.loaded = true;
6167     },
6168
6169     /** @private */
6170     closeClick : function(e){
6171         var o = {};
6172         e.stopEvent();
6173         this.fireEvent("beforeclose", this, o);
6174         if(o.cancel !== true){
6175             this.tabPanel.removeTab(this.id);
6176         }
6177     },
6178     /**
6179      * The text displayed in the tooltip for the close icon.
6180      * @type String
6181      */
6182     closeText : "Close this tab"
6183 });
6184
6185 /** @private */
6186 Roo.TabPanel.prototype.createStrip = function(container){
6187     var strip = document.createElement("div");
6188     strip.className = "x-tabs-wrap";
6189     container.appendChild(strip);
6190     return strip;
6191 };
6192 /** @private */
6193 Roo.TabPanel.prototype.createStripList = function(strip){
6194     // div wrapper for retard IE
6195     // returns the "tr" element.
6196     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6197         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6198         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6199     return strip.firstChild.firstChild.firstChild.firstChild;
6200 };
6201 /** @private */
6202 Roo.TabPanel.prototype.createBody = function(container){
6203     var body = document.createElement("div");
6204     Roo.id(body, "tab-body");
6205     Roo.fly(body).addClass("x-tabs-body");
6206     container.appendChild(body);
6207     return body;
6208 };
6209 /** @private */
6210 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6211     var body = Roo.getDom(id);
6212     if(!body){
6213         body = document.createElement("div");
6214         body.id = id;
6215     }
6216     Roo.fly(body).addClass("x-tabs-item-body");
6217     bodyEl.insertBefore(body, bodyEl.firstChild);
6218     return body;
6219 };
6220 /** @private */
6221 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6222     var td = document.createElement("td");
6223     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6224     //stripEl.appendChild(td);
6225     if(closable){
6226         td.className = "x-tabs-closable";
6227         if(!this.closeTpl){
6228             this.closeTpl = new Roo.Template(
6229                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6230                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6231                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6232             );
6233         }
6234         var el = this.closeTpl.overwrite(td, {"text": text});
6235         var close = el.getElementsByTagName("div")[0];
6236         var inner = el.getElementsByTagName("em")[0];
6237         return {"el": el, "close": close, "inner": inner};
6238     } else {
6239         if(!this.tabTpl){
6240             this.tabTpl = new Roo.Template(
6241                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6242                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6243             );
6244         }
6245         var el = this.tabTpl.overwrite(td, {"text": text});
6246         var inner = el.getElementsByTagName("em")[0];
6247         return {"el": el, "inner": inner};
6248     }
6249 };/*
6250  * Based on:
6251  * Ext JS Library 1.1.1
6252  * Copyright(c) 2006-2007, Ext JS, LLC.
6253  *
6254  * Originally Released Under LGPL - original licence link has changed is not relivant.
6255  *
6256  * Fork - LGPL
6257  * <script type="text/javascript">
6258  */
6259
6260 /**
6261  * @class Roo.Button
6262  * @extends Roo.util.Observable
6263  * Simple Button class
6264  * @cfg {String} text The button text
6265  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6266  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6267  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6268  * @cfg {Object} scope The scope of the handler
6269  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6270  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6271  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6272  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6273  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6274  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6275    applies if enableToggle = true)
6276  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6277  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6278   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6279  * @constructor
6280  * Create a new button
6281  * @param {Object} config The config object
6282  */
6283 Roo.Button = function(renderTo, config)
6284 {
6285     if (!config) {
6286         config = renderTo;
6287         renderTo = config.renderTo || false;
6288     }
6289     
6290     Roo.apply(this, config);
6291     this.addEvents({
6292         /**
6293              * @event click
6294              * Fires when this button is clicked
6295              * @param {Button} this
6296              * @param {EventObject} e The click event
6297              */
6298             "click" : true,
6299         /**
6300              * @event toggle
6301              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6302              * @param {Button} this
6303              * @param {Boolean} pressed
6304              */
6305             "toggle" : true,
6306         /**
6307              * @event mouseover
6308              * Fires when the mouse hovers over the button
6309              * @param {Button} this
6310              * @param {Event} e The event object
6311              */
6312         'mouseover' : true,
6313         /**
6314              * @event mouseout
6315              * Fires when the mouse exits the button
6316              * @param {Button} this
6317              * @param {Event} e The event object
6318              */
6319         'mouseout': true,
6320          /**
6321              * @event render
6322              * Fires when the button is rendered
6323              * @param {Button} this
6324              */
6325         'render': true
6326     });
6327     if(this.menu){
6328         this.menu = Roo.menu.MenuMgr.get(this.menu);
6329     }
6330     // register listeners first!!  - so render can be captured..
6331     Roo.util.Observable.call(this);
6332     if(renderTo){
6333         this.render(renderTo);
6334     }
6335     
6336   
6337 };
6338
6339 Roo.extend(Roo.Button, Roo.util.Observable, {
6340     /**
6341      * 
6342      */
6343     
6344     /**
6345      * Read-only. True if this button is hidden
6346      * @type Boolean
6347      */
6348     hidden : false,
6349     /**
6350      * Read-only. True if this button is disabled
6351      * @type Boolean
6352      */
6353     disabled : false,
6354     /**
6355      * Read-only. True if this button is pressed (only if enableToggle = true)
6356      * @type Boolean
6357      */
6358     pressed : false,
6359
6360     /**
6361      * @cfg {Number} tabIndex 
6362      * The DOM tabIndex for this button (defaults to undefined)
6363      */
6364     tabIndex : undefined,
6365
6366     /**
6367      * @cfg {Boolean} enableToggle
6368      * True to enable pressed/not pressed toggling (defaults to false)
6369      */
6370     enableToggle: false,
6371     /**
6372      * @cfg {Mixed} menu
6373      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6374      */
6375     menu : undefined,
6376     /**
6377      * @cfg {String} menuAlign
6378      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6379      */
6380     menuAlign : "tl-bl?",
6381
6382     /**
6383      * @cfg {String} iconCls
6384      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6385      */
6386     iconCls : undefined,
6387     /**
6388      * @cfg {String} type
6389      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6390      */
6391     type : 'button',
6392
6393     // private
6394     menuClassTarget: 'tr',
6395
6396     /**
6397      * @cfg {String} clickEvent
6398      * The type of event to map to the button's event handler (defaults to 'click')
6399      */
6400     clickEvent : 'click',
6401
6402     /**
6403      * @cfg {Boolean} handleMouseEvents
6404      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6405      */
6406     handleMouseEvents : true,
6407
6408     /**
6409      * @cfg {String} tooltipType
6410      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6411      */
6412     tooltipType : 'qtip',
6413
6414     /**
6415      * @cfg {String} cls
6416      * A CSS class to apply to the button's main element.
6417      */
6418     
6419     /**
6420      * @cfg {Roo.Template} template (Optional)
6421      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6422      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6423      * require code modifications if required elements (e.g. a button) aren't present.
6424      */
6425
6426     // private
6427     render : function(renderTo){
6428         var btn;
6429         if(this.hideParent){
6430             this.parentEl = Roo.get(renderTo);
6431         }
6432         if(!this.dhconfig){
6433             if(!this.template){
6434                 if(!Roo.Button.buttonTemplate){
6435                     // hideous table template
6436                     Roo.Button.buttonTemplate = new Roo.Template(
6437                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6438                         '<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>',
6439                         "</tr></tbody></table>");
6440                 }
6441                 this.template = Roo.Button.buttonTemplate;
6442             }
6443             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6444             var btnEl = btn.child("button:first");
6445             btnEl.on('focus', this.onFocus, this);
6446             btnEl.on('blur', this.onBlur, this);
6447             if(this.cls){
6448                 btn.addClass(this.cls);
6449             }
6450             if(this.icon){
6451                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6452             }
6453             if(this.iconCls){
6454                 btnEl.addClass(this.iconCls);
6455                 if(!this.cls){
6456                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6457                 }
6458             }
6459             if(this.tabIndex !== undefined){
6460                 btnEl.dom.tabIndex = this.tabIndex;
6461             }
6462             if(this.tooltip){
6463                 if(typeof this.tooltip == 'object'){
6464                     Roo.QuickTips.tips(Roo.apply({
6465                           target: btnEl.id
6466                     }, this.tooltip));
6467                 } else {
6468                     btnEl.dom[this.tooltipType] = this.tooltip;
6469                 }
6470             }
6471         }else{
6472             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6473         }
6474         this.el = btn;
6475         if(this.id){
6476             this.el.dom.id = this.el.id = this.id;
6477         }
6478         if(this.menu){
6479             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6480             this.menu.on("show", this.onMenuShow, this);
6481             this.menu.on("hide", this.onMenuHide, this);
6482         }
6483         btn.addClass("x-btn");
6484         if(Roo.isIE && !Roo.isIE7){
6485             this.autoWidth.defer(1, this);
6486         }else{
6487             this.autoWidth();
6488         }
6489         if(this.handleMouseEvents){
6490             btn.on("mouseover", this.onMouseOver, this);
6491             btn.on("mouseout", this.onMouseOut, this);
6492             btn.on("mousedown", this.onMouseDown, this);
6493         }
6494         btn.on(this.clickEvent, this.onClick, this);
6495         //btn.on("mouseup", this.onMouseUp, this);
6496         if(this.hidden){
6497             this.hide();
6498         }
6499         if(this.disabled){
6500             this.disable();
6501         }
6502         Roo.ButtonToggleMgr.register(this);
6503         if(this.pressed){
6504             this.el.addClass("x-btn-pressed");
6505         }
6506         if(this.repeat){
6507             var repeater = new Roo.util.ClickRepeater(btn,
6508                 typeof this.repeat == "object" ? this.repeat : {}
6509             );
6510             repeater.on("click", this.onClick,  this);
6511         }
6512         
6513         this.fireEvent('render', this);
6514         
6515     },
6516     /**
6517      * Returns the button's underlying element
6518      * @return {Roo.Element} The element
6519      */
6520     getEl : function(){
6521         return this.el;  
6522     },
6523     
6524     /**
6525      * Destroys this Button and removes any listeners.
6526      */
6527     destroy : function(){
6528         Roo.ButtonToggleMgr.unregister(this);
6529         this.el.removeAllListeners();
6530         this.purgeListeners();
6531         this.el.remove();
6532     },
6533
6534     // private
6535     autoWidth : function(){
6536         if(this.el){
6537             this.el.setWidth("auto");
6538             if(Roo.isIE7 && Roo.isStrict){
6539                 var ib = this.el.child('button');
6540                 if(ib && ib.getWidth() > 20){
6541                     ib.clip();
6542                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6543                 }
6544             }
6545             if(this.minWidth){
6546                 if(this.hidden){
6547                     this.el.beginMeasure();
6548                 }
6549                 if(this.el.getWidth() < this.minWidth){
6550                     this.el.setWidth(this.minWidth);
6551                 }
6552                 if(this.hidden){
6553                     this.el.endMeasure();
6554                 }
6555             }
6556         }
6557     },
6558
6559     /**
6560      * Assigns this button's click handler
6561      * @param {Function} handler The function to call when the button is clicked
6562      * @param {Object} scope (optional) Scope for the function passed in
6563      */
6564     setHandler : function(handler, scope){
6565         this.handler = handler;
6566         this.scope = scope;  
6567     },
6568     
6569     /**
6570      * Sets this button's text
6571      * @param {String} text The button text
6572      */
6573     setText : function(text){
6574         this.text = text;
6575         if(this.el){
6576             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6577         }
6578         this.autoWidth();
6579     },
6580     
6581     /**
6582      * Gets the text for this button
6583      * @return {String} The button text
6584      */
6585     getText : function(){
6586         return this.text;  
6587     },
6588     
6589     /**
6590      * Show this button
6591      */
6592     show: function(){
6593         this.hidden = false;
6594         if(this.el){
6595             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6596         }
6597     },
6598     
6599     /**
6600      * Hide this button
6601      */
6602     hide: function(){
6603         this.hidden = true;
6604         if(this.el){
6605             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6606         }
6607     },
6608     
6609     /**
6610      * Convenience function for boolean show/hide
6611      * @param {Boolean} visible True to show, false to hide
6612      */
6613     setVisible: function(visible){
6614         if(visible) {
6615             this.show();
6616         }else{
6617             this.hide();
6618         }
6619     },
6620     
6621     /**
6622      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6623      * @param {Boolean} state (optional) Force a particular state
6624      */
6625     toggle : function(state){
6626         state = state === undefined ? !this.pressed : state;
6627         if(state != this.pressed){
6628             if(state){
6629                 this.el.addClass("x-btn-pressed");
6630                 this.pressed = true;
6631                 this.fireEvent("toggle", this, true);
6632             }else{
6633                 this.el.removeClass("x-btn-pressed");
6634                 this.pressed = false;
6635                 this.fireEvent("toggle", this, false);
6636             }
6637             if(this.toggleHandler){
6638                 this.toggleHandler.call(this.scope || this, this, state);
6639             }
6640         }
6641     },
6642     
6643     /**
6644      * Focus the button
6645      */
6646     focus : function(){
6647         this.el.child('button:first').focus();
6648     },
6649     
6650     /**
6651      * Disable this button
6652      */
6653     disable : function(){
6654         if(this.el){
6655             this.el.addClass("x-btn-disabled");
6656         }
6657         this.disabled = true;
6658     },
6659     
6660     /**
6661      * Enable this button
6662      */
6663     enable : function(){
6664         if(this.el){
6665             this.el.removeClass("x-btn-disabled");
6666         }
6667         this.disabled = false;
6668     },
6669
6670     /**
6671      * Convenience function for boolean enable/disable
6672      * @param {Boolean} enabled True to enable, false to disable
6673      */
6674     setDisabled : function(v){
6675         this[v !== true ? "enable" : "disable"]();
6676     },
6677
6678     // private
6679     onClick : function(e)
6680     {
6681         if(e){
6682             e.preventDefault();
6683         }
6684         if(e.button != 0){
6685             return;
6686         }
6687         if(!this.disabled){
6688             if(this.enableToggle){
6689                 this.toggle();
6690             }
6691             if(this.menu && !this.menu.isVisible()){
6692                 this.menu.show(this.el, this.menuAlign);
6693             }
6694             this.fireEvent("click", this, e);
6695             if(this.handler){
6696                 this.el.removeClass("x-btn-over");
6697                 this.handler.call(this.scope || this, this, e);
6698             }
6699         }
6700     },
6701     // private
6702     onMouseOver : function(e){
6703         if(!this.disabled){
6704             this.el.addClass("x-btn-over");
6705             this.fireEvent('mouseover', this, e);
6706         }
6707     },
6708     // private
6709     onMouseOut : function(e){
6710         if(!e.within(this.el,  true)){
6711             this.el.removeClass("x-btn-over");
6712             this.fireEvent('mouseout', this, e);
6713         }
6714     },
6715     // private
6716     onFocus : function(e){
6717         if(!this.disabled){
6718             this.el.addClass("x-btn-focus");
6719         }
6720     },
6721     // private
6722     onBlur : function(e){
6723         this.el.removeClass("x-btn-focus");
6724     },
6725     // private
6726     onMouseDown : function(e){
6727         if(!this.disabled && e.button == 0){
6728             this.el.addClass("x-btn-click");
6729             Roo.get(document).on('mouseup', this.onMouseUp, this);
6730         }
6731     },
6732     // private
6733     onMouseUp : function(e){
6734         if(e.button == 0){
6735             this.el.removeClass("x-btn-click");
6736             Roo.get(document).un('mouseup', this.onMouseUp, this);
6737         }
6738     },
6739     // private
6740     onMenuShow : function(e){
6741         this.el.addClass("x-btn-menu-active");
6742     },
6743     // private
6744     onMenuHide : function(e){
6745         this.el.removeClass("x-btn-menu-active");
6746     }   
6747 });
6748
6749 // Private utility class used by Button
6750 Roo.ButtonToggleMgr = function(){
6751    var groups = {};
6752    
6753    function toggleGroup(btn, state){
6754        if(state){
6755            var g = groups[btn.toggleGroup];
6756            for(var i = 0, l = g.length; i < l; i++){
6757                if(g[i] != btn){
6758                    g[i].toggle(false);
6759                }
6760            }
6761        }
6762    }
6763    
6764    return {
6765        register : function(btn){
6766            if(!btn.toggleGroup){
6767                return;
6768            }
6769            var g = groups[btn.toggleGroup];
6770            if(!g){
6771                g = groups[btn.toggleGroup] = [];
6772            }
6773            g.push(btn);
6774            btn.on("toggle", toggleGroup);
6775        },
6776        
6777        unregister : function(btn){
6778            if(!btn.toggleGroup){
6779                return;
6780            }
6781            var g = groups[btn.toggleGroup];
6782            if(g){
6783                g.remove(btn);
6784                btn.un("toggle", toggleGroup);
6785            }
6786        }
6787    };
6788 }();/*
6789  * Based on:
6790  * Ext JS Library 1.1.1
6791  * Copyright(c) 2006-2007, Ext JS, LLC.
6792  *
6793  * Originally Released Under LGPL - original licence link has changed is not relivant.
6794  *
6795  * Fork - LGPL
6796  * <script type="text/javascript">
6797  */
6798  
6799 /**
6800  * @class Roo.SplitButton
6801  * @extends Roo.Button
6802  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6803  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
6804  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6805  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6806  * @cfg {String} arrowTooltip The title attribute of the arrow
6807  * @constructor
6808  * Create a new menu button
6809  * @param {String/HTMLElement/Element} renderTo The element to append the button to
6810  * @param {Object} config The config object
6811  */
6812 Roo.SplitButton = function(renderTo, config){
6813     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6814     /**
6815      * @event arrowclick
6816      * Fires when this button's arrow is clicked
6817      * @param {SplitButton} this
6818      * @param {EventObject} e The click event
6819      */
6820     this.addEvents({"arrowclick":true});
6821 };
6822
6823 Roo.extend(Roo.SplitButton, Roo.Button, {
6824     render : function(renderTo){
6825         // this is one sweet looking template!
6826         var tpl = new Roo.Template(
6827             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6828             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6829             '<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>',
6830             "</tbody></table></td><td>",
6831             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6832             '<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>',
6833             "</tbody></table></td></tr></table>"
6834         );
6835         var btn = tpl.append(renderTo, [this.text, this.type], true);
6836         var btnEl = btn.child("button");
6837         if(this.cls){
6838             btn.addClass(this.cls);
6839         }
6840         if(this.icon){
6841             btnEl.setStyle('background-image', 'url(' +this.icon +')');
6842         }
6843         if(this.iconCls){
6844             btnEl.addClass(this.iconCls);
6845             if(!this.cls){
6846                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6847             }
6848         }
6849         this.el = btn;
6850         if(this.handleMouseEvents){
6851             btn.on("mouseover", this.onMouseOver, this);
6852             btn.on("mouseout", this.onMouseOut, this);
6853             btn.on("mousedown", this.onMouseDown, this);
6854             btn.on("mouseup", this.onMouseUp, this);
6855         }
6856         btn.on(this.clickEvent, this.onClick, this);
6857         if(this.tooltip){
6858             if(typeof this.tooltip == 'object'){
6859                 Roo.QuickTips.tips(Roo.apply({
6860                       target: btnEl.id
6861                 }, this.tooltip));
6862             } else {
6863                 btnEl.dom[this.tooltipType] = this.tooltip;
6864             }
6865         }
6866         if(this.arrowTooltip){
6867             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6868         }
6869         if(this.hidden){
6870             this.hide();
6871         }
6872         if(this.disabled){
6873             this.disable();
6874         }
6875         if(this.pressed){
6876             this.el.addClass("x-btn-pressed");
6877         }
6878         if(Roo.isIE && !Roo.isIE7){
6879             this.autoWidth.defer(1, this);
6880         }else{
6881             this.autoWidth();
6882         }
6883         if(this.menu){
6884             this.menu.on("show", this.onMenuShow, this);
6885             this.menu.on("hide", this.onMenuHide, this);
6886         }
6887         this.fireEvent('render', this);
6888     },
6889
6890     // private
6891     autoWidth : function(){
6892         if(this.el){
6893             var tbl = this.el.child("table:first");
6894             var tbl2 = this.el.child("table:last");
6895             this.el.setWidth("auto");
6896             tbl.setWidth("auto");
6897             if(Roo.isIE7 && Roo.isStrict){
6898                 var ib = this.el.child('button:first');
6899                 if(ib && ib.getWidth() > 20){
6900                     ib.clip();
6901                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6902                 }
6903             }
6904             if(this.minWidth){
6905                 if(this.hidden){
6906                     this.el.beginMeasure();
6907                 }
6908                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6909                     tbl.setWidth(this.minWidth-tbl2.getWidth());
6910                 }
6911                 if(this.hidden){
6912                     this.el.endMeasure();
6913                 }
6914             }
6915             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6916         } 
6917     },
6918     /**
6919      * Sets this button's click handler
6920      * @param {Function} handler The function to call when the button is clicked
6921      * @param {Object} scope (optional) Scope for the function passed above
6922      */
6923     setHandler : function(handler, scope){
6924         this.handler = handler;
6925         this.scope = scope;  
6926     },
6927     
6928     /**
6929      * Sets this button's arrow click handler
6930      * @param {Function} handler The function to call when the arrow is clicked
6931      * @param {Object} scope (optional) Scope for the function passed above
6932      */
6933     setArrowHandler : function(handler, scope){
6934         this.arrowHandler = handler;
6935         this.scope = scope;  
6936     },
6937     
6938     /**
6939      * Focus the button
6940      */
6941     focus : function(){
6942         if(this.el){
6943             this.el.child("button:first").focus();
6944         }
6945     },
6946
6947     // private
6948     onClick : function(e){
6949         e.preventDefault();
6950         if(!this.disabled){
6951             if(e.getTarget(".x-btn-menu-arrow-wrap")){
6952                 if(this.menu && !this.menu.isVisible()){
6953                     this.menu.show(this.el, this.menuAlign);
6954                 }
6955                 this.fireEvent("arrowclick", this, e);
6956                 if(this.arrowHandler){
6957                     this.arrowHandler.call(this.scope || this, this, e);
6958                 }
6959             }else{
6960                 this.fireEvent("click", this, e);
6961                 if(this.handler){
6962                     this.handler.call(this.scope || this, this, e);
6963                 }
6964             }
6965         }
6966     },
6967     // private
6968     onMouseDown : function(e){
6969         if(!this.disabled){
6970             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
6971         }
6972     },
6973     // private
6974     onMouseUp : function(e){
6975         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
6976     }   
6977 });
6978
6979
6980 // backwards compat
6981 Roo.MenuButton = Roo.SplitButton;/*
6982  * Based on:
6983  * Ext JS Library 1.1.1
6984  * Copyright(c) 2006-2007, Ext JS, LLC.
6985  *
6986  * Originally Released Under LGPL - original licence link has changed is not relivant.
6987  *
6988  * Fork - LGPL
6989  * <script type="text/javascript">
6990  */
6991
6992 /**
6993  * @class Roo.Toolbar
6994  * Basic Toolbar class.
6995  * @constructor
6996  * Creates a new Toolbar
6997  * @param {Object} container The config object
6998  */ 
6999 Roo.Toolbar = function(container, buttons, config)
7000 {
7001     /// old consturctor format still supported..
7002     if(container instanceof Array){ // omit the container for later rendering
7003         buttons = container;
7004         config = buttons;
7005         container = null;
7006     }
7007     if (typeof(container) == 'object' && container.xtype) {
7008         config = container;
7009         container = config.container;
7010         buttons = config.buttons || []; // not really - use items!!
7011     }
7012     var xitems = [];
7013     if (config && config.items) {
7014         xitems = config.items;
7015         delete config.items;
7016     }
7017     Roo.apply(this, config);
7018     this.buttons = buttons;
7019     
7020     if(container){
7021         this.render(container);
7022     }
7023     this.xitems = xitems;
7024     Roo.each(xitems, function(b) {
7025         this.add(b);
7026     }, this);
7027     
7028 };
7029
7030 Roo.Toolbar.prototype = {
7031     /**
7032      * @cfg {Array} items
7033      * array of button configs or elements to add (will be converted to a MixedCollection)
7034      */
7035     items: false,
7036     /**
7037      * @cfg {String/HTMLElement/Element} container
7038      * The id or element that will contain the toolbar
7039      */
7040     // private
7041     render : function(ct){
7042         this.el = Roo.get(ct);
7043         if(this.cls){
7044             this.el.addClass(this.cls);
7045         }
7046         // using a table allows for vertical alignment
7047         // 100% width is needed by Safari...
7048         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7049         this.tr = this.el.child("tr", true);
7050         var autoId = 0;
7051         this.items = new Roo.util.MixedCollection(false, function(o){
7052             return o.id || ("item" + (++autoId));
7053         });
7054         if(this.buttons){
7055             this.add.apply(this, this.buttons);
7056             delete this.buttons;
7057         }
7058     },
7059
7060     /**
7061      * Adds element(s) to the toolbar -- this function takes a variable number of 
7062      * arguments of mixed type and adds them to the toolbar.
7063      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7064      * <ul>
7065      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7066      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7067      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7068      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7069      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7070      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7071      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7072      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7073      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7074      * </ul>
7075      * @param {Mixed} arg2
7076      * @param {Mixed} etc.
7077      */
7078     add : function(){
7079         var a = arguments, l = a.length;
7080         for(var i = 0; i < l; i++){
7081             this._add(a[i]);
7082         }
7083     },
7084     // private..
7085     _add : function(el) {
7086         
7087         if (el.xtype) {
7088             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7089         }
7090         
7091         if (el.applyTo){ // some kind of form field
7092             return this.addField(el);
7093         } 
7094         if (el.render){ // some kind of Toolbar.Item
7095             return this.addItem(el);
7096         }
7097         if (typeof el == "string"){ // string
7098             if(el == "separator" || el == "-"){
7099                 return this.addSeparator();
7100             }
7101             if (el == " "){
7102                 return this.addSpacer();
7103             }
7104             if(el == "->"){
7105                 return this.addFill();
7106             }
7107             return this.addText(el);
7108             
7109         }
7110         if(el.tagName){ // element
7111             return this.addElement(el);
7112         }
7113         if(typeof el == "object"){ // must be button config?
7114             return this.addButton(el);
7115         }
7116         // and now what?!?!
7117         return false;
7118         
7119     },
7120     
7121     /**
7122      * Add an Xtype element
7123      * @param {Object} xtype Xtype Object
7124      * @return {Object} created Object
7125      */
7126     addxtype : function(e){
7127         return this.add(e);  
7128     },
7129     
7130     /**
7131      * Returns the Element for this toolbar.
7132      * @return {Roo.Element}
7133      */
7134     getEl : function(){
7135         return this.el;  
7136     },
7137     
7138     /**
7139      * Adds a separator
7140      * @return {Roo.Toolbar.Item} The separator item
7141      */
7142     addSeparator : function(){
7143         return this.addItem(new Roo.Toolbar.Separator());
7144     },
7145
7146     /**
7147      * Adds a spacer element
7148      * @return {Roo.Toolbar.Spacer} The spacer item
7149      */
7150     addSpacer : function(){
7151         return this.addItem(new Roo.Toolbar.Spacer());
7152     },
7153
7154     /**
7155      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7156      * @return {Roo.Toolbar.Fill} The fill item
7157      */
7158     addFill : function(){
7159         return this.addItem(new Roo.Toolbar.Fill());
7160     },
7161
7162     /**
7163      * Adds any standard HTML element to the toolbar
7164      * @param {String/HTMLElement/Element} el The element or id of the element to add
7165      * @return {Roo.Toolbar.Item} The element's item
7166      */
7167     addElement : function(el){
7168         return this.addItem(new Roo.Toolbar.Item(el));
7169     },
7170     /**
7171      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7172      * @type Roo.util.MixedCollection  
7173      */
7174     items : false,
7175      
7176     /**
7177      * Adds any Toolbar.Item or subclass
7178      * @param {Roo.Toolbar.Item} item
7179      * @return {Roo.Toolbar.Item} The item
7180      */
7181     addItem : function(item){
7182         var td = this.nextBlock();
7183         item.render(td);
7184         this.items.add(item);
7185         return item;
7186     },
7187     
7188     /**
7189      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7190      * @param {Object/Array} config A button config or array of configs
7191      * @return {Roo.Toolbar.Button/Array}
7192      */
7193     addButton : function(config){
7194         if(config instanceof Array){
7195             var buttons = [];
7196             for(var i = 0, len = config.length; i < len; i++) {
7197                 buttons.push(this.addButton(config[i]));
7198             }
7199             return buttons;
7200         }
7201         var b = config;
7202         if(!(config instanceof Roo.Toolbar.Button)){
7203             b = config.split ?
7204                 new Roo.Toolbar.SplitButton(config) :
7205                 new Roo.Toolbar.Button(config);
7206         }
7207         var td = this.nextBlock();
7208         b.render(td);
7209         this.items.add(b);
7210         return b;
7211     },
7212     
7213     /**
7214      * Adds text to the toolbar
7215      * @param {String} text The text to add
7216      * @return {Roo.Toolbar.Item} The element's item
7217      */
7218     addText : function(text){
7219         return this.addItem(new Roo.Toolbar.TextItem(text));
7220     },
7221     
7222     /**
7223      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7224      * @param {Number} index The index where the item is to be inserted
7225      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7226      * @return {Roo.Toolbar.Button/Item}
7227      */
7228     insertButton : function(index, item){
7229         if(item instanceof Array){
7230             var buttons = [];
7231             for(var i = 0, len = item.length; i < len; i++) {
7232                buttons.push(this.insertButton(index + i, item[i]));
7233             }
7234             return buttons;
7235         }
7236         if (!(item instanceof Roo.Toolbar.Button)){
7237            item = new Roo.Toolbar.Button(item);
7238         }
7239         var td = document.createElement("td");
7240         this.tr.insertBefore(td, this.tr.childNodes[index]);
7241         item.render(td);
7242         this.items.insert(index, item);
7243         return item;
7244     },
7245     
7246     /**
7247      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7248      * @param {Object} config
7249      * @return {Roo.Toolbar.Item} The element's item
7250      */
7251     addDom : function(config, returnEl){
7252         var td = this.nextBlock();
7253         Roo.DomHelper.overwrite(td, config);
7254         var ti = new Roo.Toolbar.Item(td.firstChild);
7255         ti.render(td);
7256         this.items.add(ti);
7257         return ti;
7258     },
7259
7260     /**
7261      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7262      * @type Roo.util.MixedCollection  
7263      */
7264     fields : false,
7265     
7266     /**
7267      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7268      * Note: the field should not have been rendered yet. For a field that has already been
7269      * rendered, use {@link #addElement}.
7270      * @param {Roo.form.Field} field
7271      * @return {Roo.ToolbarItem}
7272      */
7273      
7274       
7275     addField : function(field) {
7276         if (!this.fields) {
7277             var autoId = 0;
7278             this.fields = new Roo.util.MixedCollection(false, function(o){
7279                 return o.id || ("item" + (++autoId));
7280             });
7281
7282         }
7283         
7284         var td = this.nextBlock();
7285         field.render(td);
7286         var ti = new Roo.Toolbar.Item(td.firstChild);
7287         ti.render(td);
7288         this.items.add(ti);
7289         this.fields.add(field);
7290         return ti;
7291     },
7292     /**
7293      * Hide the toolbar
7294      * @method hide
7295      */
7296      
7297       
7298     hide : function()
7299     {
7300         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7301         this.el.child('div').hide();
7302     },
7303     /**
7304      * Show the toolbar
7305      * @method show
7306      */
7307     show : function()
7308     {
7309         this.el.child('div').show();
7310     },
7311       
7312     // private
7313     nextBlock : function(){
7314         var td = document.createElement("td");
7315         this.tr.appendChild(td);
7316         return td;
7317     },
7318
7319     // private
7320     destroy : function(){
7321         if(this.items){ // rendered?
7322             Roo.destroy.apply(Roo, this.items.items);
7323         }
7324         if(this.fields){ // rendered?
7325             Roo.destroy.apply(Roo, this.fields.items);
7326         }
7327         Roo.Element.uncache(this.el, this.tr);
7328     }
7329 };
7330
7331 /**
7332  * @class Roo.Toolbar.Item
7333  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7334  * @constructor
7335  * Creates a new Item
7336  * @param {HTMLElement} el 
7337  */
7338 Roo.Toolbar.Item = function(el){
7339     var cfg = {};
7340     if (typeof (el.xtype) != 'undefined') {
7341         cfg = el;
7342         el = cfg.el;
7343     }
7344     
7345     this.el = Roo.getDom(el);
7346     this.id = Roo.id(this.el);
7347     this.hidden = false;
7348     
7349     this.addEvents({
7350          /**
7351              * @event render
7352              * Fires when the button is rendered
7353              * @param {Button} this
7354              */
7355         'render': true
7356     });
7357     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7358 };
7359 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7360 //Roo.Toolbar.Item.prototype = {
7361     
7362     /**
7363      * Get this item's HTML Element
7364      * @return {HTMLElement}
7365      */
7366     getEl : function(){
7367        return this.el;  
7368     },
7369
7370     // private
7371     render : function(td){
7372         
7373          this.td = td;
7374         td.appendChild(this.el);
7375         
7376         this.fireEvent('render', this);
7377     },
7378     
7379     /**
7380      * Removes and destroys this item.
7381      */
7382     destroy : function(){
7383         this.td.parentNode.removeChild(this.td);
7384     },
7385     
7386     /**
7387      * Shows this item.
7388      */
7389     show: function(){
7390         this.hidden = false;
7391         this.td.style.display = "";
7392     },
7393     
7394     /**
7395      * Hides this item.
7396      */
7397     hide: function(){
7398         this.hidden = true;
7399         this.td.style.display = "none";
7400     },
7401     
7402     /**
7403      * Convenience function for boolean show/hide.
7404      * @param {Boolean} visible true to show/false to hide
7405      */
7406     setVisible: function(visible){
7407         if(visible) {
7408             this.show();
7409         }else{
7410             this.hide();
7411         }
7412     },
7413     
7414     /**
7415      * Try to focus this item.
7416      */
7417     focus : function(){
7418         Roo.fly(this.el).focus();
7419     },
7420     
7421     /**
7422      * Disables this item.
7423      */
7424     disable : function(){
7425         Roo.fly(this.td).addClass("x-item-disabled");
7426         this.disabled = true;
7427         this.el.disabled = true;
7428     },
7429     
7430     /**
7431      * Enables this item.
7432      */
7433     enable : function(){
7434         Roo.fly(this.td).removeClass("x-item-disabled");
7435         this.disabled = false;
7436         this.el.disabled = false;
7437     }
7438 });
7439
7440
7441 /**
7442  * @class Roo.Toolbar.Separator
7443  * @extends Roo.Toolbar.Item
7444  * A simple toolbar separator class
7445  * @constructor
7446  * Creates a new Separator
7447  */
7448 Roo.Toolbar.Separator = function(cfg){
7449     
7450     var s = document.createElement("span");
7451     s.className = "ytb-sep";
7452     if (cfg) {
7453         cfg.el = s;
7454     }
7455     
7456     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7457 };
7458 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7459     enable:Roo.emptyFn,
7460     disable:Roo.emptyFn,
7461     focus:Roo.emptyFn
7462 });
7463
7464 /**
7465  * @class Roo.Toolbar.Spacer
7466  * @extends Roo.Toolbar.Item
7467  * A simple element that adds extra horizontal space to a toolbar.
7468  * @constructor
7469  * Creates a new Spacer
7470  */
7471 Roo.Toolbar.Spacer = function(cfg){
7472     var s = document.createElement("div");
7473     s.className = "ytb-spacer";
7474     if (cfg) {
7475         cfg.el = s;
7476     }
7477     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7478 };
7479 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7480     enable:Roo.emptyFn,
7481     disable:Roo.emptyFn,
7482     focus:Roo.emptyFn
7483 });
7484
7485 /**
7486  * @class Roo.Toolbar.Fill
7487  * @extends Roo.Toolbar.Spacer
7488  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7489  * @constructor
7490  * Creates a new Spacer
7491  */
7492 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7493     // private
7494     render : function(td){
7495         td.style.width = '100%';
7496         Roo.Toolbar.Fill.superclass.render.call(this, td);
7497     }
7498 });
7499
7500 /**
7501  * @class Roo.Toolbar.TextItem
7502  * @extends Roo.Toolbar.Item
7503  * A simple class that renders text directly into a toolbar.
7504  * @constructor
7505  * Creates a new TextItem
7506  * @cfg {string} text 
7507  */
7508 Roo.Toolbar.TextItem = function(cfg){
7509     var  text = cfg || "";
7510     if (typeof(cfg) == 'object') {
7511         text = cfg.text || "";
7512     }  else {
7513         cfg = null;
7514     }
7515     var s = document.createElement("span");
7516     s.className = "ytb-text";
7517     s.innerHTML = text;
7518     if (cfg) {
7519         cfg.el  = s;
7520     }
7521     
7522     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7523 };
7524 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7525     
7526      
7527     enable:Roo.emptyFn,
7528     disable:Roo.emptyFn,
7529     focus:Roo.emptyFn
7530 });
7531
7532 /**
7533  * @class Roo.Toolbar.Button
7534  * @extends Roo.Button
7535  * A button that renders into a toolbar.
7536  * @constructor
7537  * Creates a new Button
7538  * @param {Object} config A standard {@link Roo.Button} config object
7539  */
7540 Roo.Toolbar.Button = function(config){
7541     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7542 };
7543 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7544     render : function(td){
7545         this.td = td;
7546         Roo.Toolbar.Button.superclass.render.call(this, td);
7547     },
7548     
7549     /**
7550      * Removes and destroys this button
7551      */
7552     destroy : function(){
7553         Roo.Toolbar.Button.superclass.destroy.call(this);
7554         this.td.parentNode.removeChild(this.td);
7555     },
7556     
7557     /**
7558      * Shows this button
7559      */
7560     show: function(){
7561         this.hidden = false;
7562         this.td.style.display = "";
7563     },
7564     
7565     /**
7566      * Hides this button
7567      */
7568     hide: function(){
7569         this.hidden = true;
7570         this.td.style.display = "none";
7571     },
7572
7573     /**
7574      * Disables this item
7575      */
7576     disable : function(){
7577         Roo.fly(this.td).addClass("x-item-disabled");
7578         this.disabled = true;
7579     },
7580
7581     /**
7582      * Enables this item
7583      */
7584     enable : function(){
7585         Roo.fly(this.td).removeClass("x-item-disabled");
7586         this.disabled = false;
7587     }
7588 });
7589 // backwards compat
7590 Roo.ToolbarButton = Roo.Toolbar.Button;
7591
7592 /**
7593  * @class Roo.Toolbar.SplitButton
7594  * @extends Roo.SplitButton
7595  * A menu button that renders into a toolbar.
7596  * @constructor
7597  * Creates a new SplitButton
7598  * @param {Object} config A standard {@link Roo.SplitButton} config object
7599  */
7600 Roo.Toolbar.SplitButton = function(config){
7601     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7602 };
7603 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7604     render : function(td){
7605         this.td = td;
7606         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7607     },
7608     
7609     /**
7610      * Removes and destroys this button
7611      */
7612     destroy : function(){
7613         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7614         this.td.parentNode.removeChild(this.td);
7615     },
7616     
7617     /**
7618      * Shows this button
7619      */
7620     show: function(){
7621         this.hidden = false;
7622         this.td.style.display = "";
7623     },
7624     
7625     /**
7626      * Hides this button
7627      */
7628     hide: function(){
7629         this.hidden = true;
7630         this.td.style.display = "none";
7631     }
7632 });
7633
7634 // backwards compat
7635 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7636  * Based on:
7637  * Ext JS Library 1.1.1
7638  * Copyright(c) 2006-2007, Ext JS, LLC.
7639  *
7640  * Originally Released Under LGPL - original licence link has changed is not relivant.
7641  *
7642  * Fork - LGPL
7643  * <script type="text/javascript">
7644  */
7645  
7646 /**
7647  * @class Roo.PagingToolbar
7648  * @extends Roo.Toolbar
7649  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7650  * @constructor
7651  * Create a new PagingToolbar
7652  * @param {Object} config The config object
7653  */
7654 Roo.PagingToolbar = function(el, ds, config)
7655 {
7656     // old args format still supported... - xtype is prefered..
7657     if (typeof(el) == 'object' && el.xtype) {
7658         // created from xtype...
7659         config = el;
7660         ds = el.dataSource;
7661         el = config.container;
7662     }
7663     var items = [];
7664     if (config.items) {
7665         items = config.items;
7666         config.items = [];
7667     }
7668     
7669     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7670     this.ds = ds;
7671     this.cursor = 0;
7672     this.renderButtons(this.el);
7673     this.bind(ds);
7674     
7675     // supprot items array.
7676    
7677     Roo.each(items, function(e) {
7678         this.add(Roo.factory(e));
7679     },this);
7680     
7681 };
7682
7683 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7684     /**
7685      * @cfg {Roo.data.Store} dataSource
7686      * The underlying data store providing the paged data
7687      */
7688     /**
7689      * @cfg {String/HTMLElement/Element} container
7690      * container The id or element that will contain the toolbar
7691      */
7692     /**
7693      * @cfg {Boolean} displayInfo
7694      * True to display the displayMsg (defaults to false)
7695      */
7696     /**
7697      * @cfg {Number} pageSize
7698      * The number of records to display per page (defaults to 20)
7699      */
7700     pageSize: 20,
7701     /**
7702      * @cfg {String} displayMsg
7703      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7704      */
7705     displayMsg : 'Displaying {0} - {1} of {2}',
7706     /**
7707      * @cfg {String} emptyMsg
7708      * The message to display when no records are found (defaults to "No data to display")
7709      */
7710     emptyMsg : 'No data to display',
7711     /**
7712      * Customizable piece of the default paging text (defaults to "Page")
7713      * @type String
7714      */
7715     beforePageText : "Page",
7716     /**
7717      * Customizable piece of the default paging text (defaults to "of %0")
7718      * @type String
7719      */
7720     afterPageText : "of {0}",
7721     /**
7722      * Customizable piece of the default paging text (defaults to "First Page")
7723      * @type String
7724      */
7725     firstText : "First Page",
7726     /**
7727      * Customizable piece of the default paging text (defaults to "Previous Page")
7728      * @type String
7729      */
7730     prevText : "Previous Page",
7731     /**
7732      * Customizable piece of the default paging text (defaults to "Next Page")
7733      * @type String
7734      */
7735     nextText : "Next Page",
7736     /**
7737      * Customizable piece of the default paging text (defaults to "Last Page")
7738      * @type String
7739      */
7740     lastText : "Last Page",
7741     /**
7742      * Customizable piece of the default paging text (defaults to "Refresh")
7743      * @type String
7744      */
7745     refreshText : "Refresh",
7746
7747     // private
7748     renderButtons : function(el){
7749         Roo.PagingToolbar.superclass.render.call(this, el);
7750         this.first = this.addButton({
7751             tooltip: this.firstText,
7752             cls: "x-btn-icon x-grid-page-first",
7753             disabled: true,
7754             handler: this.onClick.createDelegate(this, ["first"])
7755         });
7756         this.prev = this.addButton({
7757             tooltip: this.prevText,
7758             cls: "x-btn-icon x-grid-page-prev",
7759             disabled: true,
7760             handler: this.onClick.createDelegate(this, ["prev"])
7761         });
7762         //this.addSeparator();
7763         this.add(this.beforePageText);
7764         this.field = Roo.get(this.addDom({
7765            tag: "input",
7766            type: "text",
7767            size: "3",
7768            value: "1",
7769            cls: "x-grid-page-number"
7770         }).el);
7771         this.field.on("keydown", this.onPagingKeydown, this);
7772         this.field.on("focus", function(){this.dom.select();});
7773         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7774         this.field.setHeight(18);
7775         //this.addSeparator();
7776         this.next = this.addButton({
7777             tooltip: this.nextText,
7778             cls: "x-btn-icon x-grid-page-next",
7779             disabled: true,
7780             handler: this.onClick.createDelegate(this, ["next"])
7781         });
7782         this.last = this.addButton({
7783             tooltip: this.lastText,
7784             cls: "x-btn-icon x-grid-page-last",
7785             disabled: true,
7786             handler: this.onClick.createDelegate(this, ["last"])
7787         });
7788         //this.addSeparator();
7789         this.loading = this.addButton({
7790             tooltip: this.refreshText,
7791             cls: "x-btn-icon x-grid-loading",
7792             handler: this.onClick.createDelegate(this, ["refresh"])
7793         });
7794
7795         if(this.displayInfo){
7796             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7797         }
7798     },
7799
7800     // private
7801     updateInfo : function(){
7802         if(this.displayEl){
7803             var count = this.ds.getCount();
7804             var msg = count == 0 ?
7805                 this.emptyMsg :
7806                 String.format(
7807                     this.displayMsg,
7808                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
7809                 );
7810             this.displayEl.update(msg);
7811         }
7812     },
7813
7814     // private
7815     onLoad : function(ds, r, o){
7816        this.cursor = o.params ? o.params.start : 0;
7817        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7818
7819        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7820        this.field.dom.value = ap;
7821        this.first.setDisabled(ap == 1);
7822        this.prev.setDisabled(ap == 1);
7823        this.next.setDisabled(ap == ps);
7824        this.last.setDisabled(ap == ps);
7825        this.loading.enable();
7826        this.updateInfo();
7827     },
7828
7829     // private
7830     getPageData : function(){
7831         var total = this.ds.getTotalCount();
7832         return {
7833             total : total,
7834             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7835             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7836         };
7837     },
7838
7839     // private
7840     onLoadError : function(){
7841         this.loading.enable();
7842     },
7843
7844     // private
7845     onPagingKeydown : function(e){
7846         var k = e.getKey();
7847         var d = this.getPageData();
7848         if(k == e.RETURN){
7849             var v = this.field.dom.value, pageNum;
7850             if(!v || isNaN(pageNum = parseInt(v, 10))){
7851                 this.field.dom.value = d.activePage;
7852                 return;
7853             }
7854             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7855             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7856             e.stopEvent();
7857         }
7858         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))
7859         {
7860           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7861           this.field.dom.value = pageNum;
7862           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7863           e.stopEvent();
7864         }
7865         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7866         {
7867           var v = this.field.dom.value, pageNum; 
7868           var increment = (e.shiftKey) ? 10 : 1;
7869           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7870             increment *= -1;
7871           }
7872           if(!v || isNaN(pageNum = parseInt(v, 10))) {
7873             this.field.dom.value = d.activePage;
7874             return;
7875           }
7876           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7877           {
7878             this.field.dom.value = parseInt(v, 10) + increment;
7879             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7880             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7881           }
7882           e.stopEvent();
7883         }
7884     },
7885
7886     // private
7887     beforeLoad : function(){
7888         if(this.loading){
7889             this.loading.disable();
7890         }
7891     },
7892
7893     // private
7894     onClick : function(which){
7895         var ds = this.ds;
7896         switch(which){
7897             case "first":
7898                 ds.load({params:{start: 0, limit: this.pageSize}});
7899             break;
7900             case "prev":
7901                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7902             break;
7903             case "next":
7904                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7905             break;
7906             case "last":
7907                 var total = ds.getTotalCount();
7908                 var extra = total % this.pageSize;
7909                 var lastStart = extra ? (total - extra) : total-this.pageSize;
7910                 ds.load({params:{start: lastStart, limit: this.pageSize}});
7911             break;
7912             case "refresh":
7913                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7914             break;
7915         }
7916     },
7917
7918     /**
7919      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7920      * @param {Roo.data.Store} store The data store to unbind
7921      */
7922     unbind : function(ds){
7923         ds.un("beforeload", this.beforeLoad, this);
7924         ds.un("load", this.onLoad, this);
7925         ds.un("loadexception", this.onLoadError, this);
7926         ds.un("remove", this.updateInfo, this);
7927         ds.un("add", this.updateInfo, this);
7928         this.ds = undefined;
7929     },
7930
7931     /**
7932      * Binds the paging toolbar to the specified {@link Roo.data.Store}
7933      * @param {Roo.data.Store} store The data store to bind
7934      */
7935     bind : function(ds){
7936         ds.on("beforeload", this.beforeLoad, this);
7937         ds.on("load", this.onLoad, this);
7938         ds.on("loadexception", this.onLoadError, this);
7939         ds.on("remove", this.updateInfo, this);
7940         ds.on("add", this.updateInfo, this);
7941         this.ds = ds;
7942     }
7943 });/*
7944  * Based on:
7945  * Ext JS Library 1.1.1
7946  * Copyright(c) 2006-2007, Ext JS, LLC.
7947  *
7948  * Originally Released Under LGPL - original licence link has changed is not relivant.
7949  *
7950  * Fork - LGPL
7951  * <script type="text/javascript">
7952  */
7953
7954 /**
7955  * @class Roo.Resizable
7956  * @extends Roo.util.Observable
7957  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
7958  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
7959  * 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
7960  * the element will be wrapped for you automatically.</p>
7961  * <p>Here is the list of valid resize handles:</p>
7962  * <pre>
7963 Value   Description
7964 ------  -------------------
7965  'n'     north
7966  's'     south
7967  'e'     east
7968  'w'     west
7969  'nw'    northwest
7970  'sw'    southwest
7971  'se'    southeast
7972  'ne'    northeast
7973  'hd'    horizontal drag
7974  'all'   all
7975 </pre>
7976  * <p>Here's an example showing the creation of a typical Resizable:</p>
7977  * <pre><code>
7978 var resizer = new Roo.Resizable("element-id", {
7979     handles: 'all',
7980     minWidth: 200,
7981     minHeight: 100,
7982     maxWidth: 500,
7983     maxHeight: 400,
7984     pinned: true
7985 });
7986 resizer.on("resize", myHandler);
7987 </code></pre>
7988  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
7989  * resizer.east.setDisplayed(false);</p>
7990  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
7991  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
7992  * resize operation's new size (defaults to [0, 0])
7993  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
7994  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
7995  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
7996  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
7997  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
7998  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
7999  * @cfg {Number} width The width of the element in pixels (defaults to null)
8000  * @cfg {Number} height The height of the element in pixels (defaults to null)
8001  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8002  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8003  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8004  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8005  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8006  * in favor of the handles config option (defaults to false)
8007  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8008  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8009  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8010  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8011  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8012  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8013  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8014  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8015  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8016  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8017  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8018  * @constructor
8019  * Create a new resizable component
8020  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8021  * @param {Object} config configuration options
8022   */
8023 Roo.Resizable = function(el, config)
8024 {
8025     this.el = Roo.get(el);
8026
8027     if(config && config.wrap){
8028         config.resizeChild = this.el;
8029         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8030         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8031         this.el.setStyle("overflow", "hidden");
8032         this.el.setPositioning(config.resizeChild.getPositioning());
8033         config.resizeChild.clearPositioning();
8034         if(!config.width || !config.height){
8035             var csize = config.resizeChild.getSize();
8036             this.el.setSize(csize.width, csize.height);
8037         }
8038         if(config.pinned && !config.adjustments){
8039             config.adjustments = "auto";
8040         }
8041     }
8042
8043     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8044     this.proxy.unselectable();
8045     this.proxy.enableDisplayMode('block');
8046
8047     Roo.apply(this, config);
8048
8049     if(this.pinned){
8050         this.disableTrackOver = true;
8051         this.el.addClass("x-resizable-pinned");
8052     }
8053     // if the element isn't positioned, make it relative
8054     var position = this.el.getStyle("position");
8055     if(position != "absolute" && position != "fixed"){
8056         this.el.setStyle("position", "relative");
8057     }
8058     if(!this.handles){ // no handles passed, must be legacy style
8059         this.handles = 's,e,se';
8060         if(this.multiDirectional){
8061             this.handles += ',n,w';
8062         }
8063     }
8064     if(this.handles == "all"){
8065         this.handles = "n s e w ne nw se sw";
8066     }
8067     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8068     var ps = Roo.Resizable.positions;
8069     for(var i = 0, len = hs.length; i < len; i++){
8070         if(hs[i] && ps[hs[i]]){
8071             var pos = ps[hs[i]];
8072             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8073         }
8074     }
8075     // legacy
8076     this.corner = this.southeast;
8077     
8078     // updateBox = the box can move..
8079     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8080         this.updateBox = true;
8081     }
8082
8083     this.activeHandle = null;
8084
8085     if(this.resizeChild){
8086         if(typeof this.resizeChild == "boolean"){
8087             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8088         }else{
8089             this.resizeChild = Roo.get(this.resizeChild, true);
8090         }
8091     }
8092     
8093     if(this.adjustments == "auto"){
8094         var rc = this.resizeChild;
8095         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8096         if(rc && (hw || hn)){
8097             rc.position("relative");
8098             rc.setLeft(hw ? hw.el.getWidth() : 0);
8099             rc.setTop(hn ? hn.el.getHeight() : 0);
8100         }
8101         this.adjustments = [
8102             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8103             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8104         ];
8105     }
8106
8107     if(this.draggable){
8108         this.dd = this.dynamic ?
8109             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8110         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8111     }
8112
8113     // public events
8114     this.addEvents({
8115         /**
8116          * @event beforeresize
8117          * Fired before resize is allowed. Set enabled to false to cancel resize.
8118          * @param {Roo.Resizable} this
8119          * @param {Roo.EventObject} e The mousedown event
8120          */
8121         "beforeresize" : true,
8122         /**
8123          * @event resizing
8124          * Fired a resizing.
8125          * @param {Roo.Resizable} this
8126          * @param {Number} x The new x position
8127          * @param {Number} y The new y position
8128          * @param {Number} w The new w width
8129          * @param {Number} h The new h hight
8130          * @param {Roo.EventObject} e The mouseup event
8131          */
8132         "resizing" : true,
8133         /**
8134          * @event resize
8135          * Fired after a resize.
8136          * @param {Roo.Resizable} this
8137          * @param {Number} width The new width
8138          * @param {Number} height The new height
8139          * @param {Roo.EventObject} e The mouseup event
8140          */
8141         "resize" : true
8142     });
8143
8144     if(this.width !== null && this.height !== null){
8145         this.resizeTo(this.width, this.height);
8146     }else{
8147         this.updateChildSize();
8148     }
8149     if(Roo.isIE){
8150         this.el.dom.style.zoom = 1;
8151     }
8152     Roo.Resizable.superclass.constructor.call(this);
8153 };
8154
8155 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8156         resizeChild : false,
8157         adjustments : [0, 0],
8158         minWidth : 5,
8159         minHeight : 5,
8160         maxWidth : 10000,
8161         maxHeight : 10000,
8162         enabled : true,
8163         animate : false,
8164         duration : .35,
8165         dynamic : false,
8166         handles : false,
8167         multiDirectional : false,
8168         disableTrackOver : false,
8169         easing : 'easeOutStrong',
8170         widthIncrement : 0,
8171         heightIncrement : 0,
8172         pinned : false,
8173         width : null,
8174         height : null,
8175         preserveRatio : false,
8176         transparent: false,
8177         minX: 0,
8178         minY: 0,
8179         draggable: false,
8180
8181         /**
8182          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8183          */
8184         constrainTo: undefined,
8185         /**
8186          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8187          */
8188         resizeRegion: undefined,
8189
8190
8191     /**
8192      * Perform a manual resize
8193      * @param {Number} width
8194      * @param {Number} height
8195      */
8196     resizeTo : function(width, height){
8197         this.el.setSize(width, height);
8198         this.updateChildSize();
8199         this.fireEvent("resize", this, width, height, null);
8200     },
8201
8202     // private
8203     startSizing : function(e, handle){
8204         this.fireEvent("beforeresize", this, e);
8205         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8206
8207             if(!this.overlay){
8208                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8209                 this.overlay.unselectable();
8210                 this.overlay.enableDisplayMode("block");
8211                 this.overlay.on("mousemove", this.onMouseMove, this);
8212                 this.overlay.on("mouseup", this.onMouseUp, this);
8213             }
8214             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8215
8216             this.resizing = true;
8217             this.startBox = this.el.getBox();
8218             this.startPoint = e.getXY();
8219             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8220                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8221
8222             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8223             this.overlay.show();
8224
8225             if(this.constrainTo) {
8226                 var ct = Roo.get(this.constrainTo);
8227                 this.resizeRegion = ct.getRegion().adjust(
8228                     ct.getFrameWidth('t'),
8229                     ct.getFrameWidth('l'),
8230                     -ct.getFrameWidth('b'),
8231                     -ct.getFrameWidth('r')
8232                 );
8233             }
8234
8235             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8236             this.proxy.show();
8237             this.proxy.setBox(this.startBox);
8238             if(!this.dynamic){
8239                 this.proxy.setStyle('visibility', 'visible');
8240             }
8241         }
8242     },
8243
8244     // private
8245     onMouseDown : function(handle, e){
8246         if(this.enabled){
8247             e.stopEvent();
8248             this.activeHandle = handle;
8249             this.startSizing(e, handle);
8250         }
8251     },
8252
8253     // private
8254     onMouseUp : function(e){
8255         var size = this.resizeElement();
8256         this.resizing = false;
8257         this.handleOut();
8258         this.overlay.hide();
8259         this.proxy.hide();
8260         this.fireEvent("resize", this, size.width, size.height, e);
8261     },
8262
8263     // private
8264     updateChildSize : function(){
8265         
8266         if(this.resizeChild){
8267             var el = this.el;
8268             var child = this.resizeChild;
8269             var adj = this.adjustments;
8270             if(el.dom.offsetWidth){
8271                 var b = el.getSize(true);
8272                 child.setSize(b.width+adj[0], b.height+adj[1]);
8273             }
8274             // Second call here for IE
8275             // The first call enables instant resizing and
8276             // the second call corrects scroll bars if they
8277             // exist
8278             if(Roo.isIE){
8279                 setTimeout(function(){
8280                     if(el.dom.offsetWidth){
8281                         var b = el.getSize(true);
8282                         child.setSize(b.width+adj[0], b.height+adj[1]);
8283                     }
8284                 }, 10);
8285             }
8286         }
8287     },
8288
8289     // private
8290     snap : function(value, inc, min){
8291         if(!inc || !value) {
8292             return value;
8293         }
8294         var newValue = value;
8295         var m = value % inc;
8296         if(m > 0){
8297             if(m > (inc/2)){
8298                 newValue = value + (inc-m);
8299             }else{
8300                 newValue = value - m;
8301             }
8302         }
8303         return Math.max(min, newValue);
8304     },
8305
8306     // private
8307     resizeElement : function(){
8308         var box = this.proxy.getBox();
8309         if(this.updateBox){
8310             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8311         }else{
8312             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8313         }
8314         this.updateChildSize();
8315         if(!this.dynamic){
8316             this.proxy.hide();
8317         }
8318         return box;
8319     },
8320
8321     // private
8322     constrain : function(v, diff, m, mx){
8323         if(v - diff < m){
8324             diff = v - m;
8325         }else if(v - diff > mx){
8326             diff = mx - v;
8327         }
8328         return diff;
8329     },
8330
8331     // private
8332     onMouseMove : function(e){
8333         
8334         if(this.enabled){
8335             try{// try catch so if something goes wrong the user doesn't get hung
8336
8337             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8338                 return;
8339             }
8340
8341             //var curXY = this.startPoint;
8342             var curSize = this.curSize || this.startBox;
8343             var x = this.startBox.x, y = this.startBox.y;
8344             var ox = x, oy = y;
8345             var w = curSize.width, h = curSize.height;
8346             var ow = w, oh = h;
8347             var mw = this.minWidth, mh = this.minHeight;
8348             var mxw = this.maxWidth, mxh = this.maxHeight;
8349             var wi = this.widthIncrement;
8350             var hi = this.heightIncrement;
8351
8352             var eventXY = e.getXY();
8353             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8354             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8355
8356             var pos = this.activeHandle.position;
8357
8358             switch(pos){
8359                 case "east":
8360                     w += diffX;
8361                     w = Math.min(Math.max(mw, w), mxw);
8362                     break;
8363              
8364                 case "south":
8365                     h += diffY;
8366                     h = Math.min(Math.max(mh, h), mxh);
8367                     break;
8368                 case "southeast":
8369                     w += diffX;
8370                     h += diffY;
8371                     w = Math.min(Math.max(mw, w), mxw);
8372                     h = Math.min(Math.max(mh, h), mxh);
8373                     break;
8374                 case "north":
8375                     diffY = this.constrain(h, diffY, mh, mxh);
8376                     y += diffY;
8377                     h -= diffY;
8378                     break;
8379                 case "hdrag":
8380                     
8381                     if (wi) {
8382                         var adiffX = Math.abs(diffX);
8383                         var sub = (adiffX % wi); // how much 
8384                         if (sub > (wi/2)) { // far enough to snap
8385                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8386                         } else {
8387                             // remove difference.. 
8388                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8389                         }
8390                     }
8391                     x += diffX;
8392                     x = Math.max(this.minX, x);
8393                     break;
8394                 case "west":
8395                     diffX = this.constrain(w, diffX, mw, mxw);
8396                     x += diffX;
8397                     w -= diffX;
8398                     break;
8399                 case "northeast":
8400                     w += diffX;
8401                     w = Math.min(Math.max(mw, w), mxw);
8402                     diffY = this.constrain(h, diffY, mh, mxh);
8403                     y += diffY;
8404                     h -= diffY;
8405                     break;
8406                 case "northwest":
8407                     diffX = this.constrain(w, diffX, mw, mxw);
8408                     diffY = this.constrain(h, diffY, mh, mxh);
8409                     y += diffY;
8410                     h -= diffY;
8411                     x += diffX;
8412                     w -= diffX;
8413                     break;
8414                case "southwest":
8415                     diffX = this.constrain(w, diffX, mw, mxw);
8416                     h += diffY;
8417                     h = Math.min(Math.max(mh, h), mxh);
8418                     x += diffX;
8419                     w -= diffX;
8420                     break;
8421             }
8422
8423             var sw = this.snap(w, wi, mw);
8424             var sh = this.snap(h, hi, mh);
8425             if(sw != w || sh != h){
8426                 switch(pos){
8427                     case "northeast":
8428                         y -= sh - h;
8429                     break;
8430                     case "north":
8431                         y -= sh - h;
8432                         break;
8433                     case "southwest":
8434                         x -= sw - w;
8435                     break;
8436                     case "west":
8437                         x -= sw - w;
8438                         break;
8439                     case "northwest":
8440                         x -= sw - w;
8441                         y -= sh - h;
8442                     break;
8443                 }
8444                 w = sw;
8445                 h = sh;
8446             }
8447
8448             if(this.preserveRatio){
8449                 switch(pos){
8450                     case "southeast":
8451                     case "east":
8452                         h = oh * (w/ow);
8453                         h = Math.min(Math.max(mh, h), mxh);
8454                         w = ow * (h/oh);
8455                        break;
8456                     case "south":
8457                         w = ow * (h/oh);
8458                         w = Math.min(Math.max(mw, w), mxw);
8459                         h = oh * (w/ow);
8460                         break;
8461                     case "northeast":
8462                         w = ow * (h/oh);
8463                         w = Math.min(Math.max(mw, w), mxw);
8464                         h = oh * (w/ow);
8465                     break;
8466                     case "north":
8467                         var tw = w;
8468                         w = ow * (h/oh);
8469                         w = Math.min(Math.max(mw, w), mxw);
8470                         h = oh * (w/ow);
8471                         x += (tw - w) / 2;
8472                         break;
8473                     case "southwest":
8474                         h = oh * (w/ow);
8475                         h = Math.min(Math.max(mh, h), mxh);
8476                         var tw = w;
8477                         w = ow * (h/oh);
8478                         x += tw - w;
8479                         break;
8480                     case "west":
8481                         var th = h;
8482                         h = oh * (w/ow);
8483                         h = Math.min(Math.max(mh, h), mxh);
8484                         y += (th - h) / 2;
8485                         var tw = w;
8486                         w = ow * (h/oh);
8487                         x += tw - w;
8488                        break;
8489                     case "northwest":
8490                         var tw = w;
8491                         var th = h;
8492                         h = oh * (w/ow);
8493                         h = Math.min(Math.max(mh, h), mxh);
8494                         w = ow * (h/oh);
8495                         y += th - h;
8496                         x += tw - w;
8497                        break;
8498
8499                 }
8500             }
8501             if (pos == 'hdrag') {
8502                 w = ow;
8503             }
8504             this.proxy.setBounds(x, y, w, h);
8505             if(this.dynamic){
8506                 this.resizeElement();
8507             }
8508             }catch(e){}
8509         }
8510         this.fireEvent("resizing", this, x, y, w, h, e);
8511     },
8512
8513     // private
8514     handleOver : function(){
8515         if(this.enabled){
8516             this.el.addClass("x-resizable-over");
8517         }
8518     },
8519
8520     // private
8521     handleOut : function(){
8522         if(!this.resizing){
8523             this.el.removeClass("x-resizable-over");
8524         }
8525     },
8526
8527     /**
8528      * Returns the element this component is bound to.
8529      * @return {Roo.Element}
8530      */
8531     getEl : function(){
8532         return this.el;
8533     },
8534
8535     /**
8536      * Returns the resizeChild element (or null).
8537      * @return {Roo.Element}
8538      */
8539     getResizeChild : function(){
8540         return this.resizeChild;
8541     },
8542     groupHandler : function()
8543     {
8544         
8545     },
8546     /**
8547      * Destroys this resizable. If the element was wrapped and
8548      * removeEl is not true then the element remains.
8549      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8550      */
8551     destroy : function(removeEl){
8552         this.proxy.remove();
8553         if(this.overlay){
8554             this.overlay.removeAllListeners();
8555             this.overlay.remove();
8556         }
8557         var ps = Roo.Resizable.positions;
8558         for(var k in ps){
8559             if(typeof ps[k] != "function" && this[ps[k]]){
8560                 var h = this[ps[k]];
8561                 h.el.removeAllListeners();
8562                 h.el.remove();
8563             }
8564         }
8565         if(removeEl){
8566             this.el.update("");
8567             this.el.remove();
8568         }
8569     }
8570 });
8571
8572 // private
8573 // hash to map config positions to true positions
8574 Roo.Resizable.positions = {
8575     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8576     hd: "hdrag"
8577 };
8578
8579 // private
8580 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8581     if(!this.tpl){
8582         // only initialize the template if resizable is used
8583         var tpl = Roo.DomHelper.createTemplate(
8584             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8585         );
8586         tpl.compile();
8587         Roo.Resizable.Handle.prototype.tpl = tpl;
8588     }
8589     this.position = pos;
8590     this.rz = rz;
8591     // show north drag fro topdra
8592     var handlepos = pos == 'hdrag' ? 'north' : pos;
8593     
8594     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8595     if (pos == 'hdrag') {
8596         this.el.setStyle('cursor', 'pointer');
8597     }
8598     this.el.unselectable();
8599     if(transparent){
8600         this.el.setOpacity(0);
8601     }
8602     this.el.on("mousedown", this.onMouseDown, this);
8603     if(!disableTrackOver){
8604         this.el.on("mouseover", this.onMouseOver, this);
8605         this.el.on("mouseout", this.onMouseOut, this);
8606     }
8607 };
8608
8609 // private
8610 Roo.Resizable.Handle.prototype = {
8611     afterResize : function(rz){
8612         Roo.log('after?');
8613         // do nothing
8614     },
8615     // private
8616     onMouseDown : function(e){
8617         this.rz.onMouseDown(this, e);
8618     },
8619     // private
8620     onMouseOver : function(e){
8621         this.rz.handleOver(this, e);
8622     },
8623     // private
8624     onMouseOut : function(e){
8625         this.rz.handleOut(this, e);
8626     }
8627 };/*
8628  * Based on:
8629  * Ext JS Library 1.1.1
8630  * Copyright(c) 2006-2007, Ext JS, LLC.
8631  *
8632  * Originally Released Under LGPL - original licence link has changed is not relivant.
8633  *
8634  * Fork - LGPL
8635  * <script type="text/javascript">
8636  */
8637
8638 /**
8639  * @class Roo.Editor
8640  * @extends Roo.Component
8641  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8642  * @constructor
8643  * Create a new Editor
8644  * @param {Roo.form.Field} field The Field object (or descendant)
8645  * @param {Object} config The config object
8646  */
8647 Roo.Editor = function(field, config){
8648     Roo.Editor.superclass.constructor.call(this, config);
8649     this.field = field;
8650     this.addEvents({
8651         /**
8652              * @event beforestartedit
8653              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
8654              * false from the handler of this event.
8655              * @param {Editor} this
8656              * @param {Roo.Element} boundEl The underlying element bound to this editor
8657              * @param {Mixed} value The field value being set
8658              */
8659         "beforestartedit" : true,
8660         /**
8661              * @event startedit
8662              * Fires when this editor is displayed
8663              * @param {Roo.Element} boundEl The underlying element bound to this editor
8664              * @param {Mixed} value The starting field value
8665              */
8666         "startedit" : true,
8667         /**
8668              * @event beforecomplete
8669              * Fires after a change has been made to the field, but before the change is reflected in the underlying
8670              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
8671              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8672              * event will not fire since no edit actually occurred.
8673              * @param {Editor} this
8674              * @param {Mixed} value The current field value
8675              * @param {Mixed} startValue The original field value
8676              */
8677         "beforecomplete" : true,
8678         /**
8679              * @event complete
8680              * Fires after editing is complete and any changed value has been written to the underlying field.
8681              * @param {Editor} this
8682              * @param {Mixed} value The current field value
8683              * @param {Mixed} startValue The original field value
8684              */
8685         "complete" : true,
8686         /**
8687          * @event specialkey
8688          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8689          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8690          * @param {Roo.form.Field} this
8691          * @param {Roo.EventObject} e The event object
8692          */
8693         "specialkey" : true
8694     });
8695 };
8696
8697 Roo.extend(Roo.Editor, Roo.Component, {
8698     /**
8699      * @cfg {Boolean/String} autosize
8700      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8701      * or "height" to adopt the height only (defaults to false)
8702      */
8703     /**
8704      * @cfg {Boolean} revertInvalid
8705      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8706      * validation fails (defaults to true)
8707      */
8708     /**
8709      * @cfg {Boolean} ignoreNoChange
8710      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8711      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
8712      * will never be ignored.
8713      */
8714     /**
8715      * @cfg {Boolean} hideEl
8716      * False to keep the bound element visible while the editor is displayed (defaults to true)
8717      */
8718     /**
8719      * @cfg {Mixed} value
8720      * The data value of the underlying field (defaults to "")
8721      */
8722     value : "",
8723     /**
8724      * @cfg {String} alignment
8725      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8726      */
8727     alignment: "c-c?",
8728     /**
8729      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8730      * for bottom-right shadow (defaults to "frame")
8731      */
8732     shadow : "frame",
8733     /**
8734      * @cfg {Boolean} constrain True to constrain the editor to the viewport
8735      */
8736     constrain : false,
8737     /**
8738      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8739      */
8740     completeOnEnter : false,
8741     /**
8742      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8743      */
8744     cancelOnEsc : false,
8745     /**
8746      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8747      */
8748     updateEl : false,
8749
8750     // private
8751     onRender : function(ct, position){
8752         this.el = new Roo.Layer({
8753             shadow: this.shadow,
8754             cls: "x-editor",
8755             parentEl : ct,
8756             shim : this.shim,
8757             shadowOffset:4,
8758             id: this.id,
8759             constrain: this.constrain
8760         });
8761         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8762         if(this.field.msgTarget != 'title'){
8763             this.field.msgTarget = 'qtip';
8764         }
8765         this.field.render(this.el);
8766         if(Roo.isGecko){
8767             this.field.el.dom.setAttribute('autocomplete', 'off');
8768         }
8769         this.field.on("specialkey", this.onSpecialKey, this);
8770         if(this.swallowKeys){
8771             this.field.el.swallowEvent(['keydown','keypress']);
8772         }
8773         this.field.show();
8774         this.field.on("blur", this.onBlur, this);
8775         if(this.field.grow){
8776             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
8777         }
8778     },
8779
8780     onSpecialKey : function(field, e)
8781     {
8782         //Roo.log('editor onSpecialKey');
8783         if(this.completeOnEnter && e.getKey() == e.ENTER){
8784             e.stopEvent();
8785             this.completeEdit();
8786             return;
8787         }
8788         // do not fire special key otherwise it might hide close the editor...
8789         if(e.getKey() == e.ENTER){    
8790             return;
8791         }
8792         if(this.cancelOnEsc && e.getKey() == e.ESC){
8793             this.cancelEdit();
8794             return;
8795         } 
8796         this.fireEvent('specialkey', field, e);
8797     
8798     },
8799
8800     /**
8801      * Starts the editing process and shows the editor.
8802      * @param {String/HTMLElement/Element} el The element to edit
8803      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8804       * to the innerHTML of el.
8805      */
8806     startEdit : function(el, value){
8807         if(this.editing){
8808             this.completeEdit();
8809         }
8810         this.boundEl = Roo.get(el);
8811         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8812         if(!this.rendered){
8813             this.render(this.parentEl || document.body);
8814         }
8815         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8816             return;
8817         }
8818         this.startValue = v;
8819         this.field.setValue(v);
8820         if(this.autoSize){
8821             var sz = this.boundEl.getSize();
8822             switch(this.autoSize){
8823                 case "width":
8824                 this.setSize(sz.width,  "");
8825                 break;
8826                 case "height":
8827                 this.setSize("",  sz.height);
8828                 break;
8829                 default:
8830                 this.setSize(sz.width,  sz.height);
8831             }
8832         }
8833         this.el.alignTo(this.boundEl, this.alignment);
8834         this.editing = true;
8835         if(Roo.QuickTips){
8836             Roo.QuickTips.disable();
8837         }
8838         this.show();
8839     },
8840
8841     /**
8842      * Sets the height and width of this editor.
8843      * @param {Number} width The new width
8844      * @param {Number} height The new height
8845      */
8846     setSize : function(w, h){
8847         this.field.setSize(w, h);
8848         if(this.el){
8849             this.el.sync();
8850         }
8851     },
8852
8853     /**
8854      * Realigns the editor to the bound field based on the current alignment config value.
8855      */
8856     realign : function(){
8857         this.el.alignTo(this.boundEl, this.alignment);
8858     },
8859
8860     /**
8861      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8862      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8863      */
8864     completeEdit : function(remainVisible){
8865         if(!this.editing){
8866             return;
8867         }
8868         var v = this.getValue();
8869         if(this.revertInvalid !== false && !this.field.isValid()){
8870             v = this.startValue;
8871             this.cancelEdit(true);
8872         }
8873         if(String(v) === String(this.startValue) && this.ignoreNoChange){
8874             this.editing = false;
8875             this.hide();
8876             return;
8877         }
8878         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8879             this.editing = false;
8880             if(this.updateEl && this.boundEl){
8881                 this.boundEl.update(v);
8882             }
8883             if(remainVisible !== true){
8884                 this.hide();
8885             }
8886             this.fireEvent("complete", this, v, this.startValue);
8887         }
8888     },
8889
8890     // private
8891     onShow : function(){
8892         this.el.show();
8893         if(this.hideEl !== false){
8894             this.boundEl.hide();
8895         }
8896         this.field.show();
8897         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8898             this.fixIEFocus = true;
8899             this.deferredFocus.defer(50, this);
8900         }else{
8901             this.field.focus();
8902         }
8903         this.fireEvent("startedit", this.boundEl, this.startValue);
8904     },
8905
8906     deferredFocus : function(){
8907         if(this.editing){
8908             this.field.focus();
8909         }
8910     },
8911
8912     /**
8913      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
8914      * reverted to the original starting value.
8915      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8916      * cancel (defaults to false)
8917      */
8918     cancelEdit : function(remainVisible){
8919         if(this.editing){
8920             this.setValue(this.startValue);
8921             if(remainVisible !== true){
8922                 this.hide();
8923             }
8924         }
8925     },
8926
8927     // private
8928     onBlur : function(){
8929         if(this.allowBlur !== true && this.editing){
8930             this.completeEdit();
8931         }
8932     },
8933
8934     // private
8935     onHide : function(){
8936         if(this.editing){
8937             this.completeEdit();
8938             return;
8939         }
8940         this.field.blur();
8941         if(this.field.collapse){
8942             this.field.collapse();
8943         }
8944         this.el.hide();
8945         if(this.hideEl !== false){
8946             this.boundEl.show();
8947         }
8948         if(Roo.QuickTips){
8949             Roo.QuickTips.enable();
8950         }
8951     },
8952
8953     /**
8954      * Sets the data value of the editor
8955      * @param {Mixed} value Any valid value supported by the underlying field
8956      */
8957     setValue : function(v){
8958         this.field.setValue(v);
8959     },
8960
8961     /**
8962      * Gets the data value of the editor
8963      * @return {Mixed} The data value
8964      */
8965     getValue : function(){
8966         return this.field.getValue();
8967     }
8968 });/*
8969  * Based on:
8970  * Ext JS Library 1.1.1
8971  * Copyright(c) 2006-2007, Ext JS, LLC.
8972  *
8973  * Originally Released Under LGPL - original licence link has changed is not relivant.
8974  *
8975  * Fork - LGPL
8976  * <script type="text/javascript">
8977  */
8978  
8979 /**
8980  * @class Roo.BasicDialog
8981  * @extends Roo.util.Observable
8982  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
8983  * <pre><code>
8984 var dlg = new Roo.BasicDialog("my-dlg", {
8985     height: 200,
8986     width: 300,
8987     minHeight: 100,
8988     minWidth: 150,
8989     modal: true,
8990     proxyDrag: true,
8991     shadow: true
8992 });
8993 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
8994 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
8995 dlg.addButton('Cancel', dlg.hide, dlg);
8996 dlg.show();
8997 </code></pre>
8998   <b>A Dialog should always be a direct child of the body element.</b>
8999  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9000  * @cfg {String} title Default text to display in the title bar (defaults to null)
9001  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9002  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9003  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9004  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9005  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9006  * (defaults to null with no animation)
9007  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9008  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9009  * property for valid values (defaults to 'all')
9010  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9011  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9012  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9013  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9014  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9015  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9016  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9017  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9018  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9019  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9020  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9021  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9022  * draggable = true (defaults to false)
9023  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9024  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9025  * shadow (defaults to false)
9026  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9027  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9028  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9029  * @cfg {Array} buttons Array of buttons
9030  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9031  * @constructor
9032  * Create a new BasicDialog.
9033  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9034  * @param {Object} config Configuration options
9035  */
9036 Roo.BasicDialog = function(el, config){
9037     this.el = Roo.get(el);
9038     var dh = Roo.DomHelper;
9039     if(!this.el && config && config.autoCreate){
9040         if(typeof config.autoCreate == "object"){
9041             if(!config.autoCreate.id){
9042                 config.autoCreate.id = el;
9043             }
9044             this.el = dh.append(document.body,
9045                         config.autoCreate, true);
9046         }else{
9047             this.el = dh.append(document.body,
9048                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9049         }
9050     }
9051     el = this.el;
9052     el.setDisplayed(true);
9053     el.hide = this.hideAction;
9054     this.id = el.id;
9055     el.addClass("x-dlg");
9056
9057     Roo.apply(this, config);
9058
9059     this.proxy = el.createProxy("x-dlg-proxy");
9060     this.proxy.hide = this.hideAction;
9061     this.proxy.setOpacity(.5);
9062     this.proxy.hide();
9063
9064     if(config.width){
9065         el.setWidth(config.width);
9066     }
9067     if(config.height){
9068         el.setHeight(config.height);
9069     }
9070     this.size = el.getSize();
9071     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9072         this.xy = [config.x,config.y];
9073     }else{
9074         this.xy = el.getCenterXY(true);
9075     }
9076     /** The header element @type Roo.Element */
9077     this.header = el.child("> .x-dlg-hd");
9078     /** The body element @type Roo.Element */
9079     this.body = el.child("> .x-dlg-bd");
9080     /** The footer element @type Roo.Element */
9081     this.footer = el.child("> .x-dlg-ft");
9082
9083     if(!this.header){
9084         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9085     }
9086     if(!this.body){
9087         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9088     }
9089
9090     this.header.unselectable();
9091     if(this.title){
9092         this.header.update(this.title);
9093     }
9094     // this element allows the dialog to be focused for keyboard event
9095     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9096     this.focusEl.swallowEvent("click", true);
9097
9098     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9099
9100     // wrap the body and footer for special rendering
9101     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9102     if(this.footer){
9103         this.bwrap.dom.appendChild(this.footer.dom);
9104     }
9105
9106     this.bg = this.el.createChild({
9107         tag: "div", cls:"x-dlg-bg",
9108         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9109     });
9110     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9111
9112
9113     if(this.autoScroll !== false && !this.autoTabs){
9114         this.body.setStyle("overflow", "auto");
9115     }
9116
9117     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9118
9119     if(this.closable !== false){
9120         this.el.addClass("x-dlg-closable");
9121         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9122         this.close.on("click", this.closeClick, this);
9123         this.close.addClassOnOver("x-dlg-close-over");
9124     }
9125     if(this.collapsible !== false){
9126         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9127         this.collapseBtn.on("click", this.collapseClick, this);
9128         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9129         this.header.on("dblclick", this.collapseClick, this);
9130     }
9131     if(this.resizable !== false){
9132         this.el.addClass("x-dlg-resizable");
9133         this.resizer = new Roo.Resizable(el, {
9134             minWidth: this.minWidth || 80,
9135             minHeight:this.minHeight || 80,
9136             handles: this.resizeHandles || "all",
9137             pinned: true
9138         });
9139         this.resizer.on("beforeresize", this.beforeResize, this);
9140         this.resizer.on("resize", this.onResize, this);
9141     }
9142     if(this.draggable !== false){
9143         el.addClass("x-dlg-draggable");
9144         if (!this.proxyDrag) {
9145             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9146         }
9147         else {
9148             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9149         }
9150         dd.setHandleElId(this.header.id);
9151         dd.endDrag = this.endMove.createDelegate(this);
9152         dd.startDrag = this.startMove.createDelegate(this);
9153         dd.onDrag = this.onDrag.createDelegate(this);
9154         dd.scroll = false;
9155         this.dd = dd;
9156     }
9157     if(this.modal){
9158         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9159         this.mask.enableDisplayMode("block");
9160         this.mask.hide();
9161         this.el.addClass("x-dlg-modal");
9162     }
9163     if(this.shadow){
9164         this.shadow = new Roo.Shadow({
9165             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9166             offset : this.shadowOffset
9167         });
9168     }else{
9169         this.shadowOffset = 0;
9170     }
9171     if(Roo.useShims && this.shim !== false){
9172         this.shim = this.el.createShim();
9173         this.shim.hide = this.hideAction;
9174         this.shim.hide();
9175     }else{
9176         this.shim = false;
9177     }
9178     if(this.autoTabs){
9179         this.initTabs();
9180     }
9181     if (this.buttons) { 
9182         var bts= this.buttons;
9183         this.buttons = [];
9184         Roo.each(bts, function(b) {
9185             this.addButton(b);
9186         }, this);
9187     }
9188     
9189     
9190     this.addEvents({
9191         /**
9192          * @event keydown
9193          * Fires when a key is pressed
9194          * @param {Roo.BasicDialog} this
9195          * @param {Roo.EventObject} e
9196          */
9197         "keydown" : true,
9198         /**
9199          * @event move
9200          * Fires when this dialog is moved by the user.
9201          * @param {Roo.BasicDialog} this
9202          * @param {Number} x The new page X
9203          * @param {Number} y The new page Y
9204          */
9205         "move" : true,
9206         /**
9207          * @event resize
9208          * Fires when this dialog is resized by the user.
9209          * @param {Roo.BasicDialog} this
9210          * @param {Number} width The new width
9211          * @param {Number} height The new height
9212          */
9213         "resize" : true,
9214         /**
9215          * @event beforehide
9216          * Fires before this dialog is hidden.
9217          * @param {Roo.BasicDialog} this
9218          */
9219         "beforehide" : true,
9220         /**
9221          * @event hide
9222          * Fires when this dialog is hidden.
9223          * @param {Roo.BasicDialog} this
9224          */
9225         "hide" : true,
9226         /**
9227          * @event beforeshow
9228          * Fires before this dialog is shown.
9229          * @param {Roo.BasicDialog} this
9230          */
9231         "beforeshow" : true,
9232         /**
9233          * @event show
9234          * Fires when this dialog is shown.
9235          * @param {Roo.BasicDialog} this
9236          */
9237         "show" : true
9238     });
9239     el.on("keydown", this.onKeyDown, this);
9240     el.on("mousedown", this.toFront, this);
9241     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9242     this.el.hide();
9243     Roo.DialogManager.register(this);
9244     Roo.BasicDialog.superclass.constructor.call(this);
9245 };
9246
9247 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9248     shadowOffset: Roo.isIE ? 6 : 5,
9249     minHeight: 80,
9250     minWidth: 200,
9251     minButtonWidth: 75,
9252     defaultButton: null,
9253     buttonAlign: "right",
9254     tabTag: 'div',
9255     firstShow: true,
9256
9257     /**
9258      * Sets the dialog title text
9259      * @param {String} text The title text to display
9260      * @return {Roo.BasicDialog} this
9261      */
9262     setTitle : function(text){
9263         this.header.update(text);
9264         return this;
9265     },
9266
9267     // private
9268     closeClick : function(){
9269         this.hide();
9270     },
9271
9272     // private
9273     collapseClick : function(){
9274         this[this.collapsed ? "expand" : "collapse"]();
9275     },
9276
9277     /**
9278      * Collapses the dialog to its minimized state (only the title bar is visible).
9279      * Equivalent to the user clicking the collapse dialog button.
9280      */
9281     collapse : function(){
9282         if(!this.collapsed){
9283             this.collapsed = true;
9284             this.el.addClass("x-dlg-collapsed");
9285             this.restoreHeight = this.el.getHeight();
9286             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9287         }
9288     },
9289
9290     /**
9291      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9292      * clicking the expand dialog button.
9293      */
9294     expand : function(){
9295         if(this.collapsed){
9296             this.collapsed = false;
9297             this.el.removeClass("x-dlg-collapsed");
9298             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9299         }
9300     },
9301
9302     /**
9303      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9304      * @return {Roo.TabPanel} The tabs component
9305      */
9306     initTabs : function(){
9307         var tabs = this.getTabs();
9308         while(tabs.getTab(0)){
9309             tabs.removeTab(0);
9310         }
9311         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9312             var dom = el.dom;
9313             tabs.addTab(Roo.id(dom), dom.title);
9314             dom.title = "";
9315         });
9316         tabs.activate(0);
9317         return tabs;
9318     },
9319
9320     // private
9321     beforeResize : function(){
9322         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9323     },
9324
9325     // private
9326     onResize : function(){
9327         this.refreshSize();
9328         this.syncBodyHeight();
9329         this.adjustAssets();
9330         this.focus();
9331         this.fireEvent("resize", this, this.size.width, this.size.height);
9332     },
9333
9334     // private
9335     onKeyDown : function(e){
9336         if(this.isVisible()){
9337             this.fireEvent("keydown", this, e);
9338         }
9339     },
9340
9341     /**
9342      * Resizes the dialog.
9343      * @param {Number} width
9344      * @param {Number} height
9345      * @return {Roo.BasicDialog} this
9346      */
9347     resizeTo : function(width, height){
9348         this.el.setSize(width, height);
9349         this.size = {width: width, height: height};
9350         this.syncBodyHeight();
9351         if(this.fixedcenter){
9352             this.center();
9353         }
9354         if(this.isVisible()){
9355             this.constrainXY();
9356             this.adjustAssets();
9357         }
9358         this.fireEvent("resize", this, width, height);
9359         return this;
9360     },
9361
9362
9363     /**
9364      * Resizes the dialog to fit the specified content size.
9365      * @param {Number} width
9366      * @param {Number} height
9367      * @return {Roo.BasicDialog} this
9368      */
9369     setContentSize : function(w, h){
9370         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9371         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9372         //if(!this.el.isBorderBox()){
9373             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9374             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9375         //}
9376         if(this.tabs){
9377             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9378             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9379         }
9380         this.resizeTo(w, h);
9381         return this;
9382     },
9383
9384     /**
9385      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9386      * executed in response to a particular key being pressed while the dialog is active.
9387      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9388      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9389      * @param {Function} fn The function to call
9390      * @param {Object} scope (optional) The scope of the function
9391      * @return {Roo.BasicDialog} this
9392      */
9393     addKeyListener : function(key, fn, scope){
9394         var keyCode, shift, ctrl, alt;
9395         if(typeof key == "object" && !(key instanceof Array)){
9396             keyCode = key["key"];
9397             shift = key["shift"];
9398             ctrl = key["ctrl"];
9399             alt = key["alt"];
9400         }else{
9401             keyCode = key;
9402         }
9403         var handler = function(dlg, e){
9404             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9405                 var k = e.getKey();
9406                 if(keyCode instanceof Array){
9407                     for(var i = 0, len = keyCode.length; i < len; i++){
9408                         if(keyCode[i] == k){
9409                           fn.call(scope || window, dlg, k, e);
9410                           return;
9411                         }
9412                     }
9413                 }else{
9414                     if(k == keyCode){
9415                         fn.call(scope || window, dlg, k, e);
9416                     }
9417                 }
9418             }
9419         };
9420         this.on("keydown", handler);
9421         return this;
9422     },
9423
9424     /**
9425      * Returns the TabPanel component (creates it if it doesn't exist).
9426      * Note: If you wish to simply check for the existence of tabs without creating them,
9427      * check for a null 'tabs' property.
9428      * @return {Roo.TabPanel} The tabs component
9429      */
9430     getTabs : function(){
9431         if(!this.tabs){
9432             this.el.addClass("x-dlg-auto-tabs");
9433             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9434             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9435         }
9436         return this.tabs;
9437     },
9438
9439     /**
9440      * Adds a button to the footer section of the dialog.
9441      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9442      * object or a valid Roo.DomHelper element config
9443      * @param {Function} handler The function called when the button is clicked
9444      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9445      * @return {Roo.Button} The new button
9446      */
9447     addButton : function(config, handler, scope){
9448         var dh = Roo.DomHelper;
9449         if(!this.footer){
9450             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9451         }
9452         if(!this.btnContainer){
9453             var tb = this.footer.createChild({
9454
9455                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9456                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9457             }, null, true);
9458             this.btnContainer = tb.firstChild.firstChild.firstChild;
9459         }
9460         var bconfig = {
9461             handler: handler,
9462             scope: scope,
9463             minWidth: this.minButtonWidth,
9464             hideParent:true
9465         };
9466         if(typeof config == "string"){
9467             bconfig.text = config;
9468         }else{
9469             if(config.tag){
9470                 bconfig.dhconfig = config;
9471             }else{
9472                 Roo.apply(bconfig, config);
9473             }
9474         }
9475         var fc = false;
9476         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9477             bconfig.position = Math.max(0, bconfig.position);
9478             fc = this.btnContainer.childNodes[bconfig.position];
9479         }
9480          
9481         var btn = new Roo.Button(
9482             fc ? 
9483                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9484                 : this.btnContainer.appendChild(document.createElement("td")),
9485             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9486             bconfig
9487         );
9488         this.syncBodyHeight();
9489         if(!this.buttons){
9490             /**
9491              * Array of all the buttons that have been added to this dialog via addButton
9492              * @type Array
9493              */
9494             this.buttons = [];
9495         }
9496         this.buttons.push(btn);
9497         return btn;
9498     },
9499
9500     /**
9501      * Sets the default button to be focused when the dialog is displayed.
9502      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9503      * @return {Roo.BasicDialog} this
9504      */
9505     setDefaultButton : function(btn){
9506         this.defaultButton = btn;
9507         return this;
9508     },
9509
9510     // private
9511     getHeaderFooterHeight : function(safe){
9512         var height = 0;
9513         if(this.header){
9514            height += this.header.getHeight();
9515         }
9516         if(this.footer){
9517            var fm = this.footer.getMargins();
9518             height += (this.footer.getHeight()+fm.top+fm.bottom);
9519         }
9520         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9521         height += this.centerBg.getPadding("tb");
9522         return height;
9523     },
9524
9525     // private
9526     syncBodyHeight : function()
9527     {
9528         var bd = this.body, // the text
9529             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9530             bw = this.bwrap;
9531         var height = this.size.height - this.getHeaderFooterHeight(false);
9532         bd.setHeight(height-bd.getMargins("tb"));
9533         var hh = this.header.getHeight();
9534         var h = this.size.height-hh;
9535         cb.setHeight(h);
9536         
9537         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9538         bw.setHeight(h-cb.getPadding("tb"));
9539         
9540         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9541         bd.setWidth(bw.getWidth(true));
9542         if(this.tabs){
9543             this.tabs.syncHeight();
9544             if(Roo.isIE){
9545                 this.tabs.el.repaint();
9546             }
9547         }
9548     },
9549
9550     /**
9551      * Restores the previous state of the dialog if Roo.state is configured.
9552      * @return {Roo.BasicDialog} this
9553      */
9554     restoreState : function(){
9555         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9556         if(box && box.width){
9557             this.xy = [box.x, box.y];
9558             this.resizeTo(box.width, box.height);
9559         }
9560         return this;
9561     },
9562
9563     // private
9564     beforeShow : function(){
9565         this.expand();
9566         if(this.fixedcenter){
9567             this.xy = this.el.getCenterXY(true);
9568         }
9569         if(this.modal){
9570             Roo.get(document.body).addClass("x-body-masked");
9571             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9572             this.mask.show();
9573         }
9574         this.constrainXY();
9575     },
9576
9577     // private
9578     animShow : function(){
9579         var b = Roo.get(this.animateTarget).getBox();
9580         this.proxy.setSize(b.width, b.height);
9581         this.proxy.setLocation(b.x, b.y);
9582         this.proxy.show();
9583         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9584                     true, .35, this.showEl.createDelegate(this));
9585     },
9586
9587     /**
9588      * Shows the dialog.
9589      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9590      * @return {Roo.BasicDialog} this
9591      */
9592     show : function(animateTarget){
9593         if (this.fireEvent("beforeshow", this) === false){
9594             return;
9595         }
9596         if(this.syncHeightBeforeShow){
9597             this.syncBodyHeight();
9598         }else if(this.firstShow){
9599             this.firstShow = false;
9600             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9601         }
9602         this.animateTarget = animateTarget || this.animateTarget;
9603         if(!this.el.isVisible()){
9604             this.beforeShow();
9605             if(this.animateTarget && Roo.get(this.animateTarget)){
9606                 this.animShow();
9607             }else{
9608                 this.showEl();
9609             }
9610         }
9611         return this;
9612     },
9613
9614     // private
9615     showEl : function(){
9616         this.proxy.hide();
9617         this.el.setXY(this.xy);
9618         this.el.show();
9619         this.adjustAssets(true);
9620         this.toFront();
9621         this.focus();
9622         // IE peekaboo bug - fix found by Dave Fenwick
9623         if(Roo.isIE){
9624             this.el.repaint();
9625         }
9626         this.fireEvent("show", this);
9627     },
9628
9629     /**
9630      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
9631      * dialog itself will receive focus.
9632      */
9633     focus : function(){
9634         if(this.defaultButton){
9635             this.defaultButton.focus();
9636         }else{
9637             this.focusEl.focus();
9638         }
9639     },
9640
9641     // private
9642     constrainXY : function(){
9643         if(this.constraintoviewport !== false){
9644             if(!this.viewSize){
9645                 if(this.container){
9646                     var s = this.container.getSize();
9647                     this.viewSize = [s.width, s.height];
9648                 }else{
9649                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9650                 }
9651             }
9652             var s = Roo.get(this.container||document).getScroll();
9653
9654             var x = this.xy[0], y = this.xy[1];
9655             var w = this.size.width, h = this.size.height;
9656             var vw = this.viewSize[0], vh = this.viewSize[1];
9657             // only move it if it needs it
9658             var moved = false;
9659             // first validate right/bottom
9660             if(x + w > vw+s.left){
9661                 x = vw - w;
9662                 moved = true;
9663             }
9664             if(y + h > vh+s.top){
9665                 y = vh - h;
9666                 moved = true;
9667             }
9668             // then make sure top/left isn't negative
9669             if(x < s.left){
9670                 x = s.left;
9671                 moved = true;
9672             }
9673             if(y < s.top){
9674                 y = s.top;
9675                 moved = true;
9676             }
9677             if(moved){
9678                 // cache xy
9679                 this.xy = [x, y];
9680                 if(this.isVisible()){
9681                     this.el.setLocation(x, y);
9682                     this.adjustAssets();
9683                 }
9684             }
9685         }
9686     },
9687
9688     // private
9689     onDrag : function(){
9690         if(!this.proxyDrag){
9691             this.xy = this.el.getXY();
9692             this.adjustAssets();
9693         }
9694     },
9695
9696     // private
9697     adjustAssets : function(doShow){
9698         var x = this.xy[0], y = this.xy[1];
9699         var w = this.size.width, h = this.size.height;
9700         if(doShow === true){
9701             if(this.shadow){
9702                 this.shadow.show(this.el);
9703             }
9704             if(this.shim){
9705                 this.shim.show();
9706             }
9707         }
9708         if(this.shadow && this.shadow.isVisible()){
9709             this.shadow.show(this.el);
9710         }
9711         if(this.shim && this.shim.isVisible()){
9712             this.shim.setBounds(x, y, w, h);
9713         }
9714     },
9715
9716     // private
9717     adjustViewport : function(w, h){
9718         if(!w || !h){
9719             w = Roo.lib.Dom.getViewWidth();
9720             h = Roo.lib.Dom.getViewHeight();
9721         }
9722         // cache the size
9723         this.viewSize = [w, h];
9724         if(this.modal && this.mask.isVisible()){
9725             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9726             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9727         }
9728         if(this.isVisible()){
9729             this.constrainXY();
9730         }
9731     },
9732
9733     /**
9734      * Destroys this dialog and all its supporting elements (including any tabs, shim,
9735      * shadow, proxy, mask, etc.)  Also removes all event listeners.
9736      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9737      */
9738     destroy : function(removeEl){
9739         if(this.isVisible()){
9740             this.animateTarget = null;
9741             this.hide();
9742         }
9743         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9744         if(this.tabs){
9745             this.tabs.destroy(removeEl);
9746         }
9747         Roo.destroy(
9748              this.shim,
9749              this.proxy,
9750              this.resizer,
9751              this.close,
9752              this.mask
9753         );
9754         if(this.dd){
9755             this.dd.unreg();
9756         }
9757         if(this.buttons){
9758            for(var i = 0, len = this.buttons.length; i < len; i++){
9759                this.buttons[i].destroy();
9760            }
9761         }
9762         this.el.removeAllListeners();
9763         if(removeEl === true){
9764             this.el.update("");
9765             this.el.remove();
9766         }
9767         Roo.DialogManager.unregister(this);
9768     },
9769
9770     // private
9771     startMove : function(){
9772         if(this.proxyDrag){
9773             this.proxy.show();
9774         }
9775         if(this.constraintoviewport !== false){
9776             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9777         }
9778     },
9779
9780     // private
9781     endMove : function(){
9782         if(!this.proxyDrag){
9783             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9784         }else{
9785             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9786             this.proxy.hide();
9787         }
9788         this.refreshSize();
9789         this.adjustAssets();
9790         this.focus();
9791         this.fireEvent("move", this, this.xy[0], this.xy[1]);
9792     },
9793
9794     /**
9795      * Brings this dialog to the front of any other visible dialogs
9796      * @return {Roo.BasicDialog} this
9797      */
9798     toFront : function(){
9799         Roo.DialogManager.bringToFront(this);
9800         return this;
9801     },
9802
9803     /**
9804      * Sends this dialog to the back (under) of any other visible dialogs
9805      * @return {Roo.BasicDialog} this
9806      */
9807     toBack : function(){
9808         Roo.DialogManager.sendToBack(this);
9809         return this;
9810     },
9811
9812     /**
9813      * Centers this dialog in the viewport
9814      * @return {Roo.BasicDialog} this
9815      */
9816     center : function(){
9817         var xy = this.el.getCenterXY(true);
9818         this.moveTo(xy[0], xy[1]);
9819         return this;
9820     },
9821
9822     /**
9823      * Moves the dialog's top-left corner to the specified point
9824      * @param {Number} x
9825      * @param {Number} y
9826      * @return {Roo.BasicDialog} this
9827      */
9828     moveTo : function(x, y){
9829         this.xy = [x,y];
9830         if(this.isVisible()){
9831             this.el.setXY(this.xy);
9832             this.adjustAssets();
9833         }
9834         return this;
9835     },
9836
9837     /**
9838      * Aligns the dialog to the specified element
9839      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9840      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9841      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9842      * @return {Roo.BasicDialog} this
9843      */
9844     alignTo : function(element, position, offsets){
9845         this.xy = this.el.getAlignToXY(element, position, offsets);
9846         if(this.isVisible()){
9847             this.el.setXY(this.xy);
9848             this.adjustAssets();
9849         }
9850         return this;
9851     },
9852
9853     /**
9854      * Anchors an element to another element and realigns it when the window is resized.
9855      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9856      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9857      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9858      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9859      * is a number, it is used as the buffer delay (defaults to 50ms).
9860      * @return {Roo.BasicDialog} this
9861      */
9862     anchorTo : function(el, alignment, offsets, monitorScroll){
9863         var action = function(){
9864             this.alignTo(el, alignment, offsets);
9865         };
9866         Roo.EventManager.onWindowResize(action, this);
9867         var tm = typeof monitorScroll;
9868         if(tm != 'undefined'){
9869             Roo.EventManager.on(window, 'scroll', action, this,
9870                 {buffer: tm == 'number' ? monitorScroll : 50});
9871         }
9872         action.call(this);
9873         return this;
9874     },
9875
9876     /**
9877      * Returns true if the dialog is visible
9878      * @return {Boolean}
9879      */
9880     isVisible : function(){
9881         return this.el.isVisible();
9882     },
9883
9884     // private
9885     animHide : function(callback){
9886         var b = Roo.get(this.animateTarget).getBox();
9887         this.proxy.show();
9888         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9889         this.el.hide();
9890         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9891                     this.hideEl.createDelegate(this, [callback]));
9892     },
9893
9894     /**
9895      * Hides the dialog.
9896      * @param {Function} callback (optional) Function to call when the dialog is hidden
9897      * @return {Roo.BasicDialog} this
9898      */
9899     hide : function(callback){
9900         if (this.fireEvent("beforehide", this) === false){
9901             return;
9902         }
9903         if(this.shadow){
9904             this.shadow.hide();
9905         }
9906         if(this.shim) {
9907           this.shim.hide();
9908         }
9909         // sometimes animateTarget seems to get set.. causing problems...
9910         // this just double checks..
9911         if(this.animateTarget && Roo.get(this.animateTarget)) {
9912            this.animHide(callback);
9913         }else{
9914             this.el.hide();
9915             this.hideEl(callback);
9916         }
9917         return this;
9918     },
9919
9920     // private
9921     hideEl : function(callback){
9922         this.proxy.hide();
9923         if(this.modal){
9924             this.mask.hide();
9925             Roo.get(document.body).removeClass("x-body-masked");
9926         }
9927         this.fireEvent("hide", this);
9928         if(typeof callback == "function"){
9929             callback();
9930         }
9931     },
9932
9933     // private
9934     hideAction : function(){
9935         this.setLeft("-10000px");
9936         this.setTop("-10000px");
9937         this.setStyle("visibility", "hidden");
9938     },
9939
9940     // private
9941     refreshSize : function(){
9942         this.size = this.el.getSize();
9943         this.xy = this.el.getXY();
9944         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
9945     },
9946
9947     // private
9948     // z-index is managed by the DialogManager and may be overwritten at any time
9949     setZIndex : function(index){
9950         if(this.modal){
9951             this.mask.setStyle("z-index", index);
9952         }
9953         if(this.shim){
9954             this.shim.setStyle("z-index", ++index);
9955         }
9956         if(this.shadow){
9957             this.shadow.setZIndex(++index);
9958         }
9959         this.el.setStyle("z-index", ++index);
9960         if(this.proxy){
9961             this.proxy.setStyle("z-index", ++index);
9962         }
9963         if(this.resizer){
9964             this.resizer.proxy.setStyle("z-index", ++index);
9965         }
9966
9967         this.lastZIndex = index;
9968     },
9969
9970     /**
9971      * Returns the element for this dialog
9972      * @return {Roo.Element} The underlying dialog Element
9973      */
9974     getEl : function(){
9975         return this.el;
9976     }
9977 });
9978
9979 /**
9980  * @class Roo.DialogManager
9981  * Provides global access to BasicDialogs that have been created and
9982  * support for z-indexing (layering) multiple open dialogs.
9983  */
9984 Roo.DialogManager = function(){
9985     var list = {};
9986     var accessList = [];
9987     var front = null;
9988
9989     // private
9990     var sortDialogs = function(d1, d2){
9991         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
9992     };
9993
9994     // private
9995     var orderDialogs = function(){
9996         accessList.sort(sortDialogs);
9997         var seed = Roo.DialogManager.zseed;
9998         for(var i = 0, len = accessList.length; i < len; i++){
9999             var dlg = accessList[i];
10000             if(dlg){
10001                 dlg.setZIndex(seed + (i*10));
10002             }
10003         }
10004     };
10005
10006     return {
10007         /**
10008          * The starting z-index for BasicDialogs (defaults to 9000)
10009          * @type Number The z-index value
10010          */
10011         zseed : 9000,
10012
10013         // private
10014         register : function(dlg){
10015             list[dlg.id] = dlg;
10016             accessList.push(dlg);
10017         },
10018
10019         // private
10020         unregister : function(dlg){
10021             delete list[dlg.id];
10022             var i=0;
10023             var len=0;
10024             if(!accessList.indexOf){
10025                 for(  i = 0, len = accessList.length; i < len; i++){
10026                     if(accessList[i] == dlg){
10027                         accessList.splice(i, 1);
10028                         return;
10029                     }
10030                 }
10031             }else{
10032                  i = accessList.indexOf(dlg);
10033                 if(i != -1){
10034                     accessList.splice(i, 1);
10035                 }
10036             }
10037         },
10038
10039         /**
10040          * Gets a registered dialog by id
10041          * @param {String/Object} id The id of the dialog or a dialog
10042          * @return {Roo.BasicDialog} this
10043          */
10044         get : function(id){
10045             return typeof id == "object" ? id : list[id];
10046         },
10047
10048         /**
10049          * Brings the specified dialog to the front
10050          * @param {String/Object} dlg The id of the dialog or a dialog
10051          * @return {Roo.BasicDialog} this
10052          */
10053         bringToFront : function(dlg){
10054             dlg = this.get(dlg);
10055             if(dlg != front){
10056                 front = dlg;
10057                 dlg._lastAccess = new Date().getTime();
10058                 orderDialogs();
10059             }
10060             return dlg;
10061         },
10062
10063         /**
10064          * Sends the specified dialog to the back
10065          * @param {String/Object} dlg The id of the dialog or a dialog
10066          * @return {Roo.BasicDialog} this
10067          */
10068         sendToBack : function(dlg){
10069             dlg = this.get(dlg);
10070             dlg._lastAccess = -(new Date().getTime());
10071             orderDialogs();
10072             return dlg;
10073         },
10074
10075         /**
10076          * Hides all dialogs
10077          */
10078         hideAll : function(){
10079             for(var id in list){
10080                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10081                     list[id].hide();
10082                 }
10083             }
10084         }
10085     };
10086 }();
10087
10088 /**
10089  * @class Roo.LayoutDialog
10090  * @extends Roo.BasicDialog
10091  * Dialog which provides adjustments for working with a layout in a Dialog.
10092  * Add your necessary layout config options to the dialog's config.<br>
10093  * Example usage (including a nested layout):
10094  * <pre><code>
10095 if(!dialog){
10096     dialog = new Roo.LayoutDialog("download-dlg", {
10097         modal: true,
10098         width:600,
10099         height:450,
10100         shadow:true,
10101         minWidth:500,
10102         minHeight:350,
10103         autoTabs:true,
10104         proxyDrag:true,
10105         // layout config merges with the dialog config
10106         center:{
10107             tabPosition: "top",
10108             alwaysShowTabs: true
10109         }
10110     });
10111     dialog.addKeyListener(27, dialog.hide, dialog);
10112     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10113     dialog.addButton("Build It!", this.getDownload, this);
10114
10115     // we can even add nested layouts
10116     var innerLayout = new Roo.BorderLayout("dl-inner", {
10117         east: {
10118             initialSize: 200,
10119             autoScroll:true,
10120             split:true
10121         },
10122         center: {
10123             autoScroll:true
10124         }
10125     });
10126     innerLayout.beginUpdate();
10127     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10128     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10129     innerLayout.endUpdate(true);
10130
10131     var layout = dialog.getLayout();
10132     layout.beginUpdate();
10133     layout.add("center", new Roo.ContentPanel("standard-panel",
10134                         {title: "Download the Source", fitToFrame:true}));
10135     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10136                {title: "Build your own roo.js"}));
10137     layout.getRegion("center").showPanel(sp);
10138     layout.endUpdate();
10139 }
10140 </code></pre>
10141     * @constructor
10142     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10143     * @param {Object} config configuration options
10144   */
10145 Roo.LayoutDialog = function(el, cfg){
10146     
10147     var config=  cfg;
10148     if (typeof(cfg) == 'undefined') {
10149         config = Roo.apply({}, el);
10150         // not sure why we use documentElement here.. - it should always be body.
10151         // IE7 borks horribly if we use documentElement.
10152         // webkit also does not like documentElement - it creates a body element...
10153         el = Roo.get( document.body || document.documentElement ).createChild();
10154         //config.autoCreate = true;
10155     }
10156     
10157     
10158     config.autoTabs = false;
10159     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10160     this.body.setStyle({overflow:"hidden", position:"relative"});
10161     this.layout = new Roo.BorderLayout(this.body.dom, config);
10162     this.layout.monitorWindowResize = false;
10163     this.el.addClass("x-dlg-auto-layout");
10164     // fix case when center region overwrites center function
10165     this.center = Roo.BasicDialog.prototype.center;
10166     this.on("show", this.layout.layout, this.layout, true);
10167     if (config.items) {
10168         var xitems = config.items;
10169         delete config.items;
10170         Roo.each(xitems, this.addxtype, this);
10171     }
10172     
10173     
10174 };
10175 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10176     /**
10177      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10178      * @deprecated
10179      */
10180     endUpdate : function(){
10181         this.layout.endUpdate();
10182     },
10183
10184     /**
10185      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10186      *  @deprecated
10187      */
10188     beginUpdate : function(){
10189         this.layout.beginUpdate();
10190     },
10191
10192     /**
10193      * Get the BorderLayout for this dialog
10194      * @return {Roo.BorderLayout}
10195      */
10196     getLayout : function(){
10197         return this.layout;
10198     },
10199
10200     showEl : function(){
10201         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10202         if(Roo.isIE7){
10203             this.layout.layout();
10204         }
10205     },
10206
10207     // private
10208     // Use the syncHeightBeforeShow config option to control this automatically
10209     syncBodyHeight : function(){
10210         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10211         if(this.layout){this.layout.layout();}
10212     },
10213     
10214       /**
10215      * Add an xtype element (actually adds to the layout.)
10216      * @return {Object} xdata xtype object data.
10217      */
10218     
10219     addxtype : function(c) {
10220         return this.layout.addxtype(c);
10221     }
10222 });/*
10223  * Based on:
10224  * Ext JS Library 1.1.1
10225  * Copyright(c) 2006-2007, Ext JS, LLC.
10226  *
10227  * Originally Released Under LGPL - original licence link has changed is not relivant.
10228  *
10229  * Fork - LGPL
10230  * <script type="text/javascript">
10231  */
10232  
10233 /**
10234  * @class Roo.MessageBox
10235  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10236  * Example usage:
10237  *<pre><code>
10238 // Basic alert:
10239 Roo.Msg.alert('Status', 'Changes saved successfully.');
10240
10241 // Prompt for user data:
10242 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10243     if (btn == 'ok'){
10244         // process text value...
10245     }
10246 });
10247
10248 // Show a dialog using config options:
10249 Roo.Msg.show({
10250    title:'Save Changes?',
10251    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10252    buttons: Roo.Msg.YESNOCANCEL,
10253    fn: processResult,
10254    animEl: 'elId'
10255 });
10256 </code></pre>
10257  * @singleton
10258  */
10259 Roo.MessageBox = function(){
10260     var dlg, opt, mask, waitTimer;
10261     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10262     var buttons, activeTextEl, bwidth;
10263
10264     // private
10265     var handleButton = function(button){
10266         dlg.hide();
10267         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10268     };
10269
10270     // private
10271     var handleHide = function(){
10272         if(opt && opt.cls){
10273             dlg.el.removeClass(opt.cls);
10274         }
10275         if(waitTimer){
10276             Roo.TaskMgr.stop(waitTimer);
10277             waitTimer = null;
10278         }
10279     };
10280
10281     // private
10282     var updateButtons = function(b){
10283         var width = 0;
10284         if(!b){
10285             buttons["ok"].hide();
10286             buttons["cancel"].hide();
10287             buttons["yes"].hide();
10288             buttons["no"].hide();
10289             dlg.footer.dom.style.display = 'none';
10290             return width;
10291         }
10292         dlg.footer.dom.style.display = '';
10293         for(var k in buttons){
10294             if(typeof buttons[k] != "function"){
10295                 if(b[k]){
10296                     buttons[k].show();
10297                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10298                     width += buttons[k].el.getWidth()+15;
10299                 }else{
10300                     buttons[k].hide();
10301                 }
10302             }
10303         }
10304         return width;
10305     };
10306
10307     // private
10308     var handleEsc = function(d, k, e){
10309         if(opt && opt.closable !== false){
10310             dlg.hide();
10311         }
10312         if(e){
10313             e.stopEvent();
10314         }
10315     };
10316
10317     return {
10318         /**
10319          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10320          * @return {Roo.BasicDialog} The BasicDialog element
10321          */
10322         getDialog : function(){
10323            if(!dlg){
10324                 dlg = new Roo.BasicDialog("x-msg-box", {
10325                     autoCreate : true,
10326                     shadow: true,
10327                     draggable: true,
10328                     resizable:false,
10329                     constraintoviewport:false,
10330                     fixedcenter:true,
10331                     collapsible : false,
10332                     shim:true,
10333                     modal: true,
10334                     width:400, height:100,
10335                     buttonAlign:"center",
10336                     closeClick : function(){
10337                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10338                             handleButton("no");
10339                         }else{
10340                             handleButton("cancel");
10341                         }
10342                     }
10343                 });
10344                 dlg.on("hide", handleHide);
10345                 mask = dlg.mask;
10346                 dlg.addKeyListener(27, handleEsc);
10347                 buttons = {};
10348                 var bt = this.buttonText;
10349                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10350                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10351                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10352                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10353                 bodyEl = dlg.body.createChild({
10354
10355                     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>'
10356                 });
10357                 msgEl = bodyEl.dom.firstChild;
10358                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10359                 textboxEl.enableDisplayMode();
10360                 textboxEl.addKeyListener([10,13], function(){
10361                     if(dlg.isVisible() && opt && opt.buttons){
10362                         if(opt.buttons.ok){
10363                             handleButton("ok");
10364                         }else if(opt.buttons.yes){
10365                             handleButton("yes");
10366                         }
10367                     }
10368                 });
10369                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10370                 textareaEl.enableDisplayMode();
10371                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10372                 progressEl.enableDisplayMode();
10373                 var pf = progressEl.dom.firstChild;
10374                 if (pf) {
10375                     pp = Roo.get(pf.firstChild);
10376                     pp.setHeight(pf.offsetHeight);
10377                 }
10378                 
10379             }
10380             return dlg;
10381         },
10382
10383         /**
10384          * Updates the message box body text
10385          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10386          * the XHTML-compliant non-breaking space character '&amp;#160;')
10387          * @return {Roo.MessageBox} This message box
10388          */
10389         updateText : function(text){
10390             if(!dlg.isVisible() && !opt.width){
10391                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10392             }
10393             msgEl.innerHTML = text || '&#160;';
10394       
10395             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10396             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10397             var w = Math.max(
10398                     Math.min(opt.width || cw , this.maxWidth), 
10399                     Math.max(opt.minWidth || this.minWidth, bwidth)
10400             );
10401             if(opt.prompt){
10402                 activeTextEl.setWidth(w);
10403             }
10404             if(dlg.isVisible()){
10405                 dlg.fixedcenter = false;
10406             }
10407             // to big, make it scroll. = But as usual stupid IE does not support
10408             // !important..
10409             
10410             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10411                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10412                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10413             } else {
10414                 bodyEl.dom.style.height = '';
10415                 bodyEl.dom.style.overflowY = '';
10416             }
10417             if (cw > w) {
10418                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10419             } else {
10420                 bodyEl.dom.style.overflowX = '';
10421             }
10422             
10423             dlg.setContentSize(w, bodyEl.getHeight());
10424             if(dlg.isVisible()){
10425                 dlg.fixedcenter = true;
10426             }
10427             return this;
10428         },
10429
10430         /**
10431          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10432          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10433          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10434          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10435          * @return {Roo.MessageBox} This message box
10436          */
10437         updateProgress : function(value, text){
10438             if(text){
10439                 this.updateText(text);
10440             }
10441             if (pp) { // weird bug on my firefox - for some reason this is not defined
10442                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10443             }
10444             return this;
10445         },        
10446
10447         /**
10448          * Returns true if the message box is currently displayed
10449          * @return {Boolean} True if the message box is visible, else false
10450          */
10451         isVisible : function(){
10452             return dlg && dlg.isVisible();  
10453         },
10454
10455         /**
10456          * Hides the message box if it is displayed
10457          */
10458         hide : function(){
10459             if(this.isVisible()){
10460                 dlg.hide();
10461             }  
10462         },
10463
10464         /**
10465          * Displays a new message box, or reinitializes an existing message box, based on the config options
10466          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10467          * The following config object properties are supported:
10468          * <pre>
10469 Property    Type             Description
10470 ----------  ---------------  ------------------------------------------------------------------------------------
10471 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10472                                    closes (defaults to undefined)
10473 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10474                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10475 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10476                                    progress and wait dialogs will ignore this property and always hide the
10477                                    close button as they can only be closed programmatically.
10478 cls               String           A custom CSS class to apply to the message box element
10479 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10480                                    displayed (defaults to 75)
10481 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10482                                    function will be btn (the name of the button that was clicked, if applicable,
10483                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10484                                    Progress and wait dialogs will ignore this option since they do not respond to
10485                                    user actions and can only be closed programmatically, so any required function
10486                                    should be called by the same code after it closes the dialog.
10487 icon              String           A CSS class that provides a background image to be used as an icon for
10488                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10489 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10490 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10491 modal             Boolean          False to allow user interaction with the page while the message box is
10492                                    displayed (defaults to true)
10493 msg               String           A string that will replace the existing message box body text (defaults
10494                                    to the XHTML-compliant non-breaking space character '&#160;')
10495 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10496 progress          Boolean          True to display a progress bar (defaults to false)
10497 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10498 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10499 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10500 title             String           The title text
10501 value             String           The string value to set into the active textbox element if displayed
10502 wait              Boolean          True to display a progress bar (defaults to false)
10503 width             Number           The width of the dialog in pixels
10504 </pre>
10505          *
10506          * Example usage:
10507          * <pre><code>
10508 Roo.Msg.show({
10509    title: 'Address',
10510    msg: 'Please enter your address:',
10511    width: 300,
10512    buttons: Roo.MessageBox.OKCANCEL,
10513    multiline: true,
10514    fn: saveAddress,
10515    animEl: 'addAddressBtn'
10516 });
10517 </code></pre>
10518          * @param {Object} config Configuration options
10519          * @return {Roo.MessageBox} This message box
10520          */
10521         show : function(options)
10522         {
10523             
10524             // this causes nightmares if you show one dialog after another
10525             // especially on callbacks..
10526              
10527             if(this.isVisible()){
10528                 
10529                 this.hide();
10530                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10531                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10532                 Roo.log("New Dialog Message:" +  options.msg )
10533                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10534                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10535                 
10536             }
10537             var d = this.getDialog();
10538             opt = options;
10539             d.setTitle(opt.title || "&#160;");
10540             d.close.setDisplayed(opt.closable !== false);
10541             activeTextEl = textboxEl;
10542             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10543             if(opt.prompt){
10544                 if(opt.multiline){
10545                     textboxEl.hide();
10546                     textareaEl.show();
10547                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10548                         opt.multiline : this.defaultTextHeight);
10549                     activeTextEl = textareaEl;
10550                 }else{
10551                     textboxEl.show();
10552                     textareaEl.hide();
10553                 }
10554             }else{
10555                 textboxEl.hide();
10556                 textareaEl.hide();
10557             }
10558             progressEl.setDisplayed(opt.progress === true);
10559             this.updateProgress(0);
10560             activeTextEl.dom.value = opt.value || "";
10561             if(opt.prompt){
10562                 dlg.setDefaultButton(activeTextEl);
10563             }else{
10564                 var bs = opt.buttons;
10565                 var db = null;
10566                 if(bs && bs.ok){
10567                     db = buttons["ok"];
10568                 }else if(bs && bs.yes){
10569                     db = buttons["yes"];
10570                 }
10571                 dlg.setDefaultButton(db);
10572             }
10573             bwidth = updateButtons(opt.buttons);
10574             this.updateText(opt.msg);
10575             if(opt.cls){
10576                 d.el.addClass(opt.cls);
10577             }
10578             d.proxyDrag = opt.proxyDrag === true;
10579             d.modal = opt.modal !== false;
10580             d.mask = opt.modal !== false ? mask : false;
10581             if(!d.isVisible()){
10582                 // force it to the end of the z-index stack so it gets a cursor in FF
10583                 document.body.appendChild(dlg.el.dom);
10584                 d.animateTarget = null;
10585                 d.show(options.animEl);
10586             }
10587             return this;
10588         },
10589
10590         /**
10591          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10592          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10593          * and closing the message box when the process is complete.
10594          * @param {String} title The title bar text
10595          * @param {String} msg The message box body text
10596          * @return {Roo.MessageBox} This message box
10597          */
10598         progress : function(title, msg){
10599             this.show({
10600                 title : title,
10601                 msg : msg,
10602                 buttons: false,
10603                 progress:true,
10604                 closable:false,
10605                 minWidth: this.minProgressWidth,
10606                 modal : true
10607             });
10608             return this;
10609         },
10610
10611         /**
10612          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10613          * If a callback function is passed it will be called after the user clicks the button, and the
10614          * id of the button that was clicked will be passed as the only parameter to the callback
10615          * (could also be the top-right close button).
10616          * @param {String} title The title bar text
10617          * @param {String} msg The message box body text
10618          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10619          * @param {Object} scope (optional) The scope of the callback function
10620          * @return {Roo.MessageBox} This message box
10621          */
10622         alert : function(title, msg, fn, scope){
10623             this.show({
10624                 title : title,
10625                 msg : msg,
10626                 buttons: this.OK,
10627                 fn: fn,
10628                 scope : scope,
10629                 modal : true
10630             });
10631             return this;
10632         },
10633
10634         /**
10635          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10636          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10637          * You are responsible for closing the message box when the process is complete.
10638          * @param {String} msg The message box body text
10639          * @param {String} title (optional) The title bar text
10640          * @return {Roo.MessageBox} This message box
10641          */
10642         wait : function(msg, title){
10643             this.show({
10644                 title : title,
10645                 msg : msg,
10646                 buttons: false,
10647                 closable:false,
10648                 progress:true,
10649                 modal:true,
10650                 width:300,
10651                 wait:true
10652             });
10653             waitTimer = Roo.TaskMgr.start({
10654                 run: function(i){
10655                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10656                 },
10657                 interval: 1000
10658             });
10659             return this;
10660         },
10661
10662         /**
10663          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10664          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10665          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10666          * @param {String} title The title bar text
10667          * @param {String} msg The message box body text
10668          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10669          * @param {Object} scope (optional) The scope of the callback function
10670          * @return {Roo.MessageBox} This message box
10671          */
10672         confirm : function(title, msg, fn, scope){
10673             this.show({
10674                 title : title,
10675                 msg : msg,
10676                 buttons: this.YESNO,
10677                 fn: fn,
10678                 scope : scope,
10679                 modal : true
10680             });
10681             return this;
10682         },
10683
10684         /**
10685          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10686          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10687          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10688          * (could also be the top-right close button) and the text that was entered will be passed as the two
10689          * parameters to the callback.
10690          * @param {String} title The title bar text
10691          * @param {String} msg The message box body text
10692          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10693          * @param {Object} scope (optional) The scope of the callback function
10694          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10695          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10696          * @return {Roo.MessageBox} This message box
10697          */
10698         prompt : function(title, msg, fn, scope, multiline){
10699             this.show({
10700                 title : title,
10701                 msg : msg,
10702                 buttons: this.OKCANCEL,
10703                 fn: fn,
10704                 minWidth:250,
10705                 scope : scope,
10706                 prompt:true,
10707                 multiline: multiline,
10708                 modal : true
10709             });
10710             return this;
10711         },
10712
10713         /**
10714          * Button config that displays a single OK button
10715          * @type Object
10716          */
10717         OK : {ok:true},
10718         /**
10719          * Button config that displays Yes and No buttons
10720          * @type Object
10721          */
10722         YESNO : {yes:true, no:true},
10723         /**
10724          * Button config that displays OK and Cancel buttons
10725          * @type Object
10726          */
10727         OKCANCEL : {ok:true, cancel:true},
10728         /**
10729          * Button config that displays Yes, No and Cancel buttons
10730          * @type Object
10731          */
10732         YESNOCANCEL : {yes:true, no:true, cancel:true},
10733
10734         /**
10735          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10736          * @type Number
10737          */
10738         defaultTextHeight : 75,
10739         /**
10740          * The maximum width in pixels of the message box (defaults to 600)
10741          * @type Number
10742          */
10743         maxWidth : 600,
10744         /**
10745          * The minimum width in pixels of the message box (defaults to 100)
10746          * @type Number
10747          */
10748         minWidth : 100,
10749         /**
10750          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10751          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10752          * @type Number
10753          */
10754         minProgressWidth : 250,
10755         /**
10756          * An object containing the default button text strings that can be overriden for localized language support.
10757          * Supported properties are: ok, cancel, yes and no.
10758          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10759          * @type Object
10760          */
10761         buttonText : {
10762             ok : "OK",
10763             cancel : "Cancel",
10764             yes : "Yes",
10765             no : "No"
10766         }
10767     };
10768 }();
10769
10770 /**
10771  * Shorthand for {@link Roo.MessageBox}
10772  */
10773 Roo.Msg = Roo.MessageBox;/*
10774  * Based on:
10775  * Ext JS Library 1.1.1
10776  * Copyright(c) 2006-2007, Ext JS, LLC.
10777  *
10778  * Originally Released Under LGPL - original licence link has changed is not relivant.
10779  *
10780  * Fork - LGPL
10781  * <script type="text/javascript">
10782  */
10783 /**
10784  * @class Roo.QuickTips
10785  * Provides attractive and customizable tooltips for any element.
10786  * @singleton
10787  */
10788 Roo.QuickTips = function(){
10789     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10790     var ce, bd, xy, dd;
10791     var visible = false, disabled = true, inited = false;
10792     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10793     
10794     var onOver = function(e){
10795         if(disabled){
10796             return;
10797         }
10798         var t = e.getTarget();
10799         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10800             return;
10801         }
10802         if(ce && t == ce.el){
10803             clearTimeout(hideProc);
10804             return;
10805         }
10806         if(t && tagEls[t.id]){
10807             tagEls[t.id].el = t;
10808             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10809             return;
10810         }
10811         var ttp, et = Roo.fly(t);
10812         var ns = cfg.namespace;
10813         if(tm.interceptTitles && t.title){
10814             ttp = t.title;
10815             t.qtip = ttp;
10816             t.removeAttribute("title");
10817             e.preventDefault();
10818         }else{
10819             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10820         }
10821         if(ttp){
10822             showProc = show.defer(tm.showDelay, tm, [{
10823                 el: t, 
10824                 text: ttp.replace(/\\n/g,'<br/>'),
10825                 width: et.getAttributeNS(ns, cfg.width),
10826                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10827                 title: et.getAttributeNS(ns, cfg.title),
10828                     cls: et.getAttributeNS(ns, cfg.cls)
10829             }]);
10830         }
10831     };
10832     
10833     var onOut = function(e){
10834         clearTimeout(showProc);
10835         var t = e.getTarget();
10836         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10837             hideProc = setTimeout(hide, tm.hideDelay);
10838         }
10839     };
10840     
10841     var onMove = function(e){
10842         if(disabled){
10843             return;
10844         }
10845         xy = e.getXY();
10846         xy[1] += 18;
10847         if(tm.trackMouse && ce){
10848             el.setXY(xy);
10849         }
10850     };
10851     
10852     var onDown = function(e){
10853         clearTimeout(showProc);
10854         clearTimeout(hideProc);
10855         if(!e.within(el)){
10856             if(tm.hideOnClick){
10857                 hide();
10858                 tm.disable();
10859                 tm.enable.defer(100, tm);
10860             }
10861         }
10862     };
10863     
10864     var getPad = function(){
10865         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10866     };
10867
10868     var show = function(o){
10869         if(disabled){
10870             return;
10871         }
10872         clearTimeout(dismissProc);
10873         ce = o;
10874         if(removeCls){ // in case manually hidden
10875             el.removeClass(removeCls);
10876             removeCls = null;
10877         }
10878         if(ce.cls){
10879             el.addClass(ce.cls);
10880             removeCls = ce.cls;
10881         }
10882         if(ce.title){
10883             tipTitle.update(ce.title);
10884             tipTitle.show();
10885         }else{
10886             tipTitle.update('');
10887             tipTitle.hide();
10888         }
10889         el.dom.style.width  = tm.maxWidth+'px';
10890         //tipBody.dom.style.width = '';
10891         tipBodyText.update(o.text);
10892         var p = getPad(), w = ce.width;
10893         if(!w){
10894             var td = tipBodyText.dom;
10895             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10896             if(aw > tm.maxWidth){
10897                 w = tm.maxWidth;
10898             }else if(aw < tm.minWidth){
10899                 w = tm.minWidth;
10900             }else{
10901                 w = aw;
10902             }
10903         }
10904         //tipBody.setWidth(w);
10905         el.setWidth(parseInt(w, 10) + p);
10906         if(ce.autoHide === false){
10907             close.setDisplayed(true);
10908             if(dd){
10909                 dd.unlock();
10910             }
10911         }else{
10912             close.setDisplayed(false);
10913             if(dd){
10914                 dd.lock();
10915             }
10916         }
10917         if(xy){
10918             el.avoidY = xy[1]-18;
10919             el.setXY(xy);
10920         }
10921         if(tm.animate){
10922             el.setOpacity(.1);
10923             el.setStyle("visibility", "visible");
10924             el.fadeIn({callback: afterShow});
10925         }else{
10926             afterShow();
10927         }
10928     };
10929     
10930     var afterShow = function(){
10931         if(ce){
10932             el.show();
10933             esc.enable();
10934             if(tm.autoDismiss && ce.autoHide !== false){
10935                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
10936             }
10937         }
10938     };
10939     
10940     var hide = function(noanim){
10941         clearTimeout(dismissProc);
10942         clearTimeout(hideProc);
10943         ce = null;
10944         if(el.isVisible()){
10945             esc.disable();
10946             if(noanim !== true && tm.animate){
10947                 el.fadeOut({callback: afterHide});
10948             }else{
10949                 afterHide();
10950             } 
10951         }
10952     };
10953     
10954     var afterHide = function(){
10955         el.hide();
10956         if(removeCls){
10957             el.removeClass(removeCls);
10958             removeCls = null;
10959         }
10960     };
10961     
10962     return {
10963         /**
10964         * @cfg {Number} minWidth
10965         * The minimum width of the quick tip (defaults to 40)
10966         */
10967        minWidth : 40,
10968         /**
10969         * @cfg {Number} maxWidth
10970         * The maximum width of the quick tip (defaults to 300)
10971         */
10972        maxWidth : 300,
10973         /**
10974         * @cfg {Boolean} interceptTitles
10975         * True to automatically use the element's DOM title value if available (defaults to false)
10976         */
10977        interceptTitles : false,
10978         /**
10979         * @cfg {Boolean} trackMouse
10980         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
10981         */
10982        trackMouse : false,
10983         /**
10984         * @cfg {Boolean} hideOnClick
10985         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
10986         */
10987        hideOnClick : true,
10988         /**
10989         * @cfg {Number} showDelay
10990         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
10991         */
10992        showDelay : 500,
10993         /**
10994         * @cfg {Number} hideDelay
10995         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
10996         */
10997        hideDelay : 200,
10998         /**
10999         * @cfg {Boolean} autoHide
11000         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11001         * Used in conjunction with hideDelay.
11002         */
11003        autoHide : true,
11004         /**
11005         * @cfg {Boolean}
11006         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11007         * (defaults to true).  Used in conjunction with autoDismissDelay.
11008         */
11009        autoDismiss : true,
11010         /**
11011         * @cfg {Number}
11012         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11013         */
11014        autoDismissDelay : 5000,
11015        /**
11016         * @cfg {Boolean} animate
11017         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11018         */
11019        animate : false,
11020
11021        /**
11022         * @cfg {String} title
11023         * Title text to display (defaults to '').  This can be any valid HTML markup.
11024         */
11025         title: '',
11026        /**
11027         * @cfg {String} text
11028         * Body text to display (defaults to '').  This can be any valid HTML markup.
11029         */
11030         text : '',
11031        /**
11032         * @cfg {String} cls
11033         * A CSS class to apply to the base quick tip element (defaults to '').
11034         */
11035         cls : '',
11036        /**
11037         * @cfg {Number} width
11038         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11039         * minWidth or maxWidth.
11040         */
11041         width : null,
11042
11043     /**
11044      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11045      * or display QuickTips in a page.
11046      */
11047        init : function(){
11048           tm = Roo.QuickTips;
11049           cfg = tm.tagConfig;
11050           if(!inited){
11051               if(!Roo.isReady){ // allow calling of init() before onReady
11052                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11053                   return;
11054               }
11055               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11056               el.fxDefaults = {stopFx: true};
11057               // maximum custom styling
11058               //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>');
11059               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>');              
11060               tipTitle = el.child('h3');
11061               tipTitle.enableDisplayMode("block");
11062               tipBody = el.child('div.x-tip-bd');
11063               tipBodyText = el.child('div.x-tip-bd-inner');
11064               //bdLeft = el.child('div.x-tip-bd-left');
11065               //bdRight = el.child('div.x-tip-bd-right');
11066               close = el.child('div.x-tip-close');
11067               close.enableDisplayMode("block");
11068               close.on("click", hide);
11069               var d = Roo.get(document);
11070               d.on("mousedown", onDown);
11071               d.on("mouseover", onOver);
11072               d.on("mouseout", onOut);
11073               d.on("mousemove", onMove);
11074               esc = d.addKeyListener(27, hide);
11075               esc.disable();
11076               if(Roo.dd.DD){
11077                   dd = el.initDD("default", null, {
11078                       onDrag : function(){
11079                           el.sync();  
11080                       }
11081                   });
11082                   dd.setHandleElId(tipTitle.id);
11083                   dd.lock();
11084               }
11085               inited = true;
11086           }
11087           this.enable(); 
11088        },
11089
11090     /**
11091      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11092      * are supported:
11093      * <pre>
11094 Property    Type                   Description
11095 ----------  ---------------------  ------------------------------------------------------------------------
11096 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11097      * </ul>
11098      * @param {Object} config The config object
11099      */
11100        register : function(config){
11101            var cs = config instanceof Array ? config : arguments;
11102            for(var i = 0, len = cs.length; i < len; i++) {
11103                var c = cs[i];
11104                var target = c.target;
11105                if(target){
11106                    if(target instanceof Array){
11107                        for(var j = 0, jlen = target.length; j < jlen; j++){
11108                            tagEls[target[j]] = c;
11109                        }
11110                    }else{
11111                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11112                    }
11113                }
11114            }
11115        },
11116
11117     /**
11118      * Removes this quick tip from its element and destroys it.
11119      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11120      */
11121        unregister : function(el){
11122            delete tagEls[Roo.id(el)];
11123        },
11124
11125     /**
11126      * Enable this quick tip.
11127      */
11128        enable : function(){
11129            if(inited && disabled){
11130                locks.pop();
11131                if(locks.length < 1){
11132                    disabled = false;
11133                }
11134            }
11135        },
11136
11137     /**
11138      * Disable this quick tip.
11139      */
11140        disable : function(){
11141           disabled = true;
11142           clearTimeout(showProc);
11143           clearTimeout(hideProc);
11144           clearTimeout(dismissProc);
11145           if(ce){
11146               hide(true);
11147           }
11148           locks.push(1);
11149        },
11150
11151     /**
11152      * Returns true if the quick tip is enabled, else false.
11153      */
11154        isEnabled : function(){
11155             return !disabled;
11156        },
11157
11158         // private
11159        tagConfig : {
11160            namespace : "roo", // was ext?? this may break..
11161            alt_namespace : "ext",
11162            attribute : "qtip",
11163            width : "width",
11164            target : "target",
11165            title : "qtitle",
11166            hide : "hide",
11167            cls : "qclass"
11168        }
11169    };
11170 }();
11171
11172 // backwards compat
11173 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11174  * Based on:
11175  * Ext JS Library 1.1.1
11176  * Copyright(c) 2006-2007, Ext JS, LLC.
11177  *
11178  * Originally Released Under LGPL - original licence link has changed is not relivant.
11179  *
11180  * Fork - LGPL
11181  * <script type="text/javascript">
11182  */
11183  
11184
11185 /**
11186  * @class Roo.tree.TreePanel
11187  * @extends Roo.data.Tree
11188
11189  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11190  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11191  * @cfg {Boolean} enableDD true to enable drag and drop
11192  * @cfg {Boolean} enableDrag true to enable just drag
11193  * @cfg {Boolean} enableDrop true to enable just drop
11194  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11195  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11196  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11197  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11198  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11199  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11200  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11201  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11202  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11203  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11204  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11205  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11206  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11207  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11208  * @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>
11209  * @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>
11210  * 
11211  * @constructor
11212  * @param {String/HTMLElement/Element} el The container element
11213  * @param {Object} config
11214  */
11215 Roo.tree.TreePanel = function(el, config){
11216     var root = false;
11217     var loader = false;
11218     if (config.root) {
11219         root = config.root;
11220         delete config.root;
11221     }
11222     if (config.loader) {
11223         loader = config.loader;
11224         delete config.loader;
11225     }
11226     
11227     Roo.apply(this, config);
11228     Roo.tree.TreePanel.superclass.constructor.call(this);
11229     this.el = Roo.get(el);
11230     this.el.addClass('x-tree');
11231     //console.log(root);
11232     if (root) {
11233         this.setRootNode( Roo.factory(root, Roo.tree));
11234     }
11235     if (loader) {
11236         this.loader = Roo.factory(loader, Roo.tree);
11237     }
11238    /**
11239     * Read-only. The id of the container element becomes this TreePanel's id.
11240     */
11241     this.id = this.el.id;
11242     this.addEvents({
11243         /**
11244         * @event beforeload
11245         * Fires before a node is loaded, return false to cancel
11246         * @param {Node} node The node being loaded
11247         */
11248         "beforeload" : true,
11249         /**
11250         * @event load
11251         * Fires when a node is loaded
11252         * @param {Node} node The node that was loaded
11253         */
11254         "load" : true,
11255         /**
11256         * @event textchange
11257         * Fires when the text for a node is changed
11258         * @param {Node} node The node
11259         * @param {String} text The new text
11260         * @param {String} oldText The old text
11261         */
11262         "textchange" : true,
11263         /**
11264         * @event beforeexpand
11265         * Fires before a node is expanded, return false to cancel.
11266         * @param {Node} node The node
11267         * @param {Boolean} deep
11268         * @param {Boolean} anim
11269         */
11270         "beforeexpand" : true,
11271         /**
11272         * @event beforecollapse
11273         * Fires before a node is collapsed, return false to cancel.
11274         * @param {Node} node The node
11275         * @param {Boolean} deep
11276         * @param {Boolean} anim
11277         */
11278         "beforecollapse" : true,
11279         /**
11280         * @event expand
11281         * Fires when a node is expanded
11282         * @param {Node} node The node
11283         */
11284         "expand" : true,
11285         /**
11286         * @event disabledchange
11287         * Fires when the disabled status of a node changes
11288         * @param {Node} node The node
11289         * @param {Boolean} disabled
11290         */
11291         "disabledchange" : true,
11292         /**
11293         * @event collapse
11294         * Fires when a node is collapsed
11295         * @param {Node} node The node
11296         */
11297         "collapse" : true,
11298         /**
11299         * @event beforeclick
11300         * Fires before click processing on a node. Return false to cancel the default action.
11301         * @param {Node} node The node
11302         * @param {Roo.EventObject} e The event object
11303         */
11304         "beforeclick":true,
11305         /**
11306         * @event checkchange
11307         * Fires when a node with a checkbox's checked property changes
11308         * @param {Node} this This node
11309         * @param {Boolean} checked
11310         */
11311         "checkchange":true,
11312         /**
11313         * @event click
11314         * Fires when a node is clicked
11315         * @param {Node} node The node
11316         * @param {Roo.EventObject} e The event object
11317         */
11318         "click":true,
11319         /**
11320         * @event dblclick
11321         * Fires when a node is double clicked
11322         * @param {Node} node The node
11323         * @param {Roo.EventObject} e The event object
11324         */
11325         "dblclick":true,
11326         /**
11327         * @event contextmenu
11328         * Fires when a node is right clicked
11329         * @param {Node} node The node
11330         * @param {Roo.EventObject} e The event object
11331         */
11332         "contextmenu":true,
11333         /**
11334         * @event beforechildrenrendered
11335         * Fires right before the child nodes for a node are rendered
11336         * @param {Node} node The node
11337         */
11338         "beforechildrenrendered":true,
11339         /**
11340         * @event startdrag
11341         * Fires when a node starts being dragged
11342         * @param {Roo.tree.TreePanel} this
11343         * @param {Roo.tree.TreeNode} node
11344         * @param {event} e The raw browser event
11345         */ 
11346        "startdrag" : true,
11347        /**
11348         * @event enddrag
11349         * Fires when a drag operation is complete
11350         * @param {Roo.tree.TreePanel} this
11351         * @param {Roo.tree.TreeNode} node
11352         * @param {event} e The raw browser event
11353         */
11354        "enddrag" : true,
11355        /**
11356         * @event dragdrop
11357         * Fires when a dragged node is dropped on a valid DD target
11358         * @param {Roo.tree.TreePanel} this
11359         * @param {Roo.tree.TreeNode} node
11360         * @param {DD} dd The dd it was dropped on
11361         * @param {event} e The raw browser event
11362         */
11363        "dragdrop" : true,
11364        /**
11365         * @event beforenodedrop
11366         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11367         * passed to handlers has the following properties:<br />
11368         * <ul style="padding:5px;padding-left:16px;">
11369         * <li>tree - The TreePanel</li>
11370         * <li>target - The node being targeted for the drop</li>
11371         * <li>data - The drag data from the drag source</li>
11372         * <li>point - The point of the drop - append, above or below</li>
11373         * <li>source - The drag source</li>
11374         * <li>rawEvent - Raw mouse event</li>
11375         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11376         * to be inserted by setting them on this object.</li>
11377         * <li>cancel - Set this to true to cancel the drop.</li>
11378         * </ul>
11379         * @param {Object} dropEvent
11380         */
11381        "beforenodedrop" : true,
11382        /**
11383         * @event nodedrop
11384         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11385         * passed to handlers has the following properties:<br />
11386         * <ul style="padding:5px;padding-left:16px;">
11387         * <li>tree - The TreePanel</li>
11388         * <li>target - The node being targeted for the drop</li>
11389         * <li>data - The drag data from the drag source</li>
11390         * <li>point - The point of the drop - append, above or below</li>
11391         * <li>source - The drag source</li>
11392         * <li>rawEvent - Raw mouse event</li>
11393         * <li>dropNode - Dropped node(s).</li>
11394         * </ul>
11395         * @param {Object} dropEvent
11396         */
11397        "nodedrop" : true,
11398         /**
11399         * @event nodedragover
11400         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11401         * passed to handlers has the following properties:<br />
11402         * <ul style="padding:5px;padding-left:16px;">
11403         * <li>tree - The TreePanel</li>
11404         * <li>target - The node being targeted for the drop</li>
11405         * <li>data - The drag data from the drag source</li>
11406         * <li>point - The point of the drop - append, above or below</li>
11407         * <li>source - The drag source</li>
11408         * <li>rawEvent - Raw mouse event</li>
11409         * <li>dropNode - Drop node(s) provided by the source.</li>
11410         * <li>cancel - Set this to true to signal drop not allowed.</li>
11411         * </ul>
11412         * @param {Object} dragOverEvent
11413         */
11414        "nodedragover" : true,
11415        /**
11416         * @event appendnode
11417         * Fires when append node to the tree
11418         * @param {Roo.tree.TreePanel} this
11419         * @param {Roo.tree.TreeNode} node
11420         * @param {Number} index The index of the newly appended node
11421         */
11422        "appendnode" : true
11423         
11424     });
11425     if(this.singleExpand){
11426        this.on("beforeexpand", this.restrictExpand, this);
11427     }
11428     if (this.editor) {
11429         this.editor.tree = this;
11430         this.editor = Roo.factory(this.editor, Roo.tree);
11431     }
11432     
11433     if (this.selModel) {
11434         this.selModel = Roo.factory(this.selModel, Roo.tree);
11435     }
11436    
11437 };
11438 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11439     rootVisible : true,
11440     animate: Roo.enableFx,
11441     lines : true,
11442     enableDD : false,
11443     hlDrop : Roo.enableFx,
11444   
11445     renderer: false,
11446     
11447     rendererTip: false,
11448     // private
11449     restrictExpand : function(node){
11450         var p = node.parentNode;
11451         if(p){
11452             if(p.expandedChild && p.expandedChild.parentNode == p){
11453                 p.expandedChild.collapse();
11454             }
11455             p.expandedChild = node;
11456         }
11457     },
11458
11459     // private override
11460     setRootNode : function(node){
11461         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11462         if(!this.rootVisible){
11463             node.ui = new Roo.tree.RootTreeNodeUI(node);
11464         }
11465         return node;
11466     },
11467
11468     /**
11469      * Returns the container element for this TreePanel
11470      */
11471     getEl : function(){
11472         return this.el;
11473     },
11474
11475     /**
11476      * Returns the default TreeLoader for this TreePanel
11477      */
11478     getLoader : function(){
11479         return this.loader;
11480     },
11481
11482     /**
11483      * Expand all nodes
11484      */
11485     expandAll : function(){
11486         this.root.expand(true);
11487     },
11488
11489     /**
11490      * Collapse all nodes
11491      */
11492     collapseAll : function(){
11493         this.root.collapse(true);
11494     },
11495
11496     /**
11497      * Returns the selection model used by this TreePanel
11498      */
11499     getSelectionModel : function(){
11500         if(!this.selModel){
11501             this.selModel = new Roo.tree.DefaultSelectionModel();
11502         }
11503         return this.selModel;
11504     },
11505
11506     /**
11507      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11508      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11509      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11510      * @return {Array}
11511      */
11512     getChecked : function(a, startNode){
11513         startNode = startNode || this.root;
11514         var r = [];
11515         var f = function(){
11516             if(this.attributes.checked){
11517                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11518             }
11519         }
11520         startNode.cascade(f);
11521         return r;
11522     },
11523
11524     /**
11525      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11526      * @param {String} path
11527      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11528      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11529      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11530      */
11531     expandPath : function(path, attr, callback){
11532         attr = attr || "id";
11533         var keys = path.split(this.pathSeparator);
11534         var curNode = this.root;
11535         if(curNode.attributes[attr] != keys[1]){ // invalid root
11536             if(callback){
11537                 callback(false, null);
11538             }
11539             return;
11540         }
11541         var index = 1;
11542         var f = function(){
11543             if(++index == keys.length){
11544                 if(callback){
11545                     callback(true, curNode);
11546                 }
11547                 return;
11548             }
11549             var c = curNode.findChild(attr, keys[index]);
11550             if(!c){
11551                 if(callback){
11552                     callback(false, curNode);
11553                 }
11554                 return;
11555             }
11556             curNode = c;
11557             c.expand(false, false, f);
11558         };
11559         curNode.expand(false, false, f);
11560     },
11561
11562     /**
11563      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11564      * @param {String} path
11565      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11566      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11567      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11568      */
11569     selectPath : function(path, attr, callback){
11570         attr = attr || "id";
11571         var keys = path.split(this.pathSeparator);
11572         var v = keys.pop();
11573         if(keys.length > 0){
11574             var f = function(success, node){
11575                 if(success && node){
11576                     var n = node.findChild(attr, v);
11577                     if(n){
11578                         n.select();
11579                         if(callback){
11580                             callback(true, n);
11581                         }
11582                     }else if(callback){
11583                         callback(false, n);
11584                     }
11585                 }else{
11586                     if(callback){
11587                         callback(false, n);
11588                     }
11589                 }
11590             };
11591             this.expandPath(keys.join(this.pathSeparator), attr, f);
11592         }else{
11593             this.root.select();
11594             if(callback){
11595                 callback(true, this.root);
11596             }
11597         }
11598     },
11599
11600     getTreeEl : function(){
11601         return this.el;
11602     },
11603
11604     /**
11605      * Trigger rendering of this TreePanel
11606      */
11607     render : function(){
11608         if (this.innerCt) {
11609             return this; // stop it rendering more than once!!
11610         }
11611         
11612         this.innerCt = this.el.createChild({tag:"ul",
11613                cls:"x-tree-root-ct " +
11614                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11615
11616         if(this.containerScroll){
11617             Roo.dd.ScrollManager.register(this.el);
11618         }
11619         if((this.enableDD || this.enableDrop) && !this.dropZone){
11620            /**
11621             * The dropZone used by this tree if drop is enabled
11622             * @type Roo.tree.TreeDropZone
11623             */
11624              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11625                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11626            });
11627         }
11628         if((this.enableDD || this.enableDrag) && !this.dragZone){
11629            /**
11630             * The dragZone used by this tree if drag is enabled
11631             * @type Roo.tree.TreeDragZone
11632             */
11633             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11634                ddGroup: this.ddGroup || "TreeDD",
11635                scroll: this.ddScroll
11636            });
11637         }
11638         this.getSelectionModel().init(this);
11639         if (!this.root) {
11640             Roo.log("ROOT not set in tree");
11641             return this;
11642         }
11643         this.root.render();
11644         if(!this.rootVisible){
11645             this.root.renderChildren();
11646         }
11647         return this;
11648     }
11649 });/*
11650  * Based on:
11651  * Ext JS Library 1.1.1
11652  * Copyright(c) 2006-2007, Ext JS, LLC.
11653  *
11654  * Originally Released Under LGPL - original licence link has changed is not relivant.
11655  *
11656  * Fork - LGPL
11657  * <script type="text/javascript">
11658  */
11659  
11660
11661 /**
11662  * @class Roo.tree.DefaultSelectionModel
11663  * @extends Roo.util.Observable
11664  * The default single selection for a TreePanel.
11665  * @param {Object} cfg Configuration
11666  */
11667 Roo.tree.DefaultSelectionModel = function(cfg){
11668    this.selNode = null;
11669    
11670    
11671    
11672    this.addEvents({
11673        /**
11674         * @event selectionchange
11675         * Fires when the selected node changes
11676         * @param {DefaultSelectionModel} this
11677         * @param {TreeNode} node the new selection
11678         */
11679        "selectionchange" : true,
11680
11681        /**
11682         * @event beforeselect
11683         * Fires before the selected node changes, return false to cancel the change
11684         * @param {DefaultSelectionModel} this
11685         * @param {TreeNode} node the new selection
11686         * @param {TreeNode} node the old selection
11687         */
11688        "beforeselect" : true
11689    });
11690    
11691     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11692 };
11693
11694 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11695     init : function(tree){
11696         this.tree = tree;
11697         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11698         tree.on("click", this.onNodeClick, this);
11699     },
11700     
11701     onNodeClick : function(node, e){
11702         if (e.ctrlKey && this.selNode == node)  {
11703             this.unselect(node);
11704             return;
11705         }
11706         this.select(node);
11707     },
11708     
11709     /**
11710      * Select a node.
11711      * @param {TreeNode} node The node to select
11712      * @return {TreeNode} The selected node
11713      */
11714     select : function(node){
11715         var last = this.selNode;
11716         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11717             if(last){
11718                 last.ui.onSelectedChange(false);
11719             }
11720             this.selNode = node;
11721             node.ui.onSelectedChange(true);
11722             this.fireEvent("selectionchange", this, node, last);
11723         }
11724         return node;
11725     },
11726     
11727     /**
11728      * Deselect a node.
11729      * @param {TreeNode} node The node to unselect
11730      */
11731     unselect : function(node){
11732         if(this.selNode == node){
11733             this.clearSelections();
11734         }    
11735     },
11736     
11737     /**
11738      * Clear all selections
11739      */
11740     clearSelections : function(){
11741         var n = this.selNode;
11742         if(n){
11743             n.ui.onSelectedChange(false);
11744             this.selNode = null;
11745             this.fireEvent("selectionchange", this, null);
11746         }
11747         return n;
11748     },
11749     
11750     /**
11751      * Get the selected node
11752      * @return {TreeNode} The selected node
11753      */
11754     getSelectedNode : function(){
11755         return this.selNode;    
11756     },
11757     
11758     /**
11759      * Returns true if the node is selected
11760      * @param {TreeNode} node The node to check
11761      * @return {Boolean}
11762      */
11763     isSelected : function(node){
11764         return this.selNode == node;  
11765     },
11766
11767     /**
11768      * Selects the node above the selected node in the tree, intelligently walking the nodes
11769      * @return TreeNode The new selection
11770      */
11771     selectPrevious : function(){
11772         var s = this.selNode || this.lastSelNode;
11773         if(!s){
11774             return null;
11775         }
11776         var ps = s.previousSibling;
11777         if(ps){
11778             if(!ps.isExpanded() || ps.childNodes.length < 1){
11779                 return this.select(ps);
11780             } else{
11781                 var lc = ps.lastChild;
11782                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11783                     lc = lc.lastChild;
11784                 }
11785                 return this.select(lc);
11786             }
11787         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11788             return this.select(s.parentNode);
11789         }
11790         return null;
11791     },
11792
11793     /**
11794      * Selects the node above the selected node in the tree, intelligently walking the nodes
11795      * @return TreeNode The new selection
11796      */
11797     selectNext : function(){
11798         var s = this.selNode || this.lastSelNode;
11799         if(!s){
11800             return null;
11801         }
11802         if(s.firstChild && s.isExpanded()){
11803              return this.select(s.firstChild);
11804          }else if(s.nextSibling){
11805              return this.select(s.nextSibling);
11806          }else if(s.parentNode){
11807             var newS = null;
11808             s.parentNode.bubble(function(){
11809                 if(this.nextSibling){
11810                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11811                     return false;
11812                 }
11813             });
11814             return newS;
11815          }
11816         return null;
11817     },
11818
11819     onKeyDown : function(e){
11820         var s = this.selNode || this.lastSelNode;
11821         // undesirable, but required
11822         var sm = this;
11823         if(!s){
11824             return;
11825         }
11826         var k = e.getKey();
11827         switch(k){
11828              case e.DOWN:
11829                  e.stopEvent();
11830                  this.selectNext();
11831              break;
11832              case e.UP:
11833                  e.stopEvent();
11834                  this.selectPrevious();
11835              break;
11836              case e.RIGHT:
11837                  e.preventDefault();
11838                  if(s.hasChildNodes()){
11839                      if(!s.isExpanded()){
11840                          s.expand();
11841                      }else if(s.firstChild){
11842                          this.select(s.firstChild, e);
11843                      }
11844                  }
11845              break;
11846              case e.LEFT:
11847                  e.preventDefault();
11848                  if(s.hasChildNodes() && s.isExpanded()){
11849                      s.collapse();
11850                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11851                      this.select(s.parentNode, e);
11852                  }
11853              break;
11854         };
11855     }
11856 });
11857
11858 /**
11859  * @class Roo.tree.MultiSelectionModel
11860  * @extends Roo.util.Observable
11861  * Multi selection for a TreePanel.
11862  * @param {Object} cfg Configuration
11863  */
11864 Roo.tree.MultiSelectionModel = function(){
11865    this.selNodes = [];
11866    this.selMap = {};
11867    this.addEvents({
11868        /**
11869         * @event selectionchange
11870         * Fires when the selected nodes change
11871         * @param {MultiSelectionModel} this
11872         * @param {Array} nodes Array of the selected nodes
11873         */
11874        "selectionchange" : true
11875    });
11876    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11877    
11878 };
11879
11880 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11881     init : function(tree){
11882         this.tree = tree;
11883         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11884         tree.on("click", this.onNodeClick, this);
11885     },
11886     
11887     onNodeClick : function(node, e){
11888         this.select(node, e, e.ctrlKey);
11889     },
11890     
11891     /**
11892      * Select a node.
11893      * @param {TreeNode} node The node to select
11894      * @param {EventObject} e (optional) An event associated with the selection
11895      * @param {Boolean} keepExisting True to retain existing selections
11896      * @return {TreeNode} The selected node
11897      */
11898     select : function(node, e, keepExisting){
11899         if(keepExisting !== true){
11900             this.clearSelections(true);
11901         }
11902         if(this.isSelected(node)){
11903             this.lastSelNode = node;
11904             return node;
11905         }
11906         this.selNodes.push(node);
11907         this.selMap[node.id] = node;
11908         this.lastSelNode = node;
11909         node.ui.onSelectedChange(true);
11910         this.fireEvent("selectionchange", this, this.selNodes);
11911         return node;
11912     },
11913     
11914     /**
11915      * Deselect a node.
11916      * @param {TreeNode} node The node to unselect
11917      */
11918     unselect : function(node){
11919         if(this.selMap[node.id]){
11920             node.ui.onSelectedChange(false);
11921             var sn = this.selNodes;
11922             var index = -1;
11923             if(sn.indexOf){
11924                 index = sn.indexOf(node);
11925             }else{
11926                 for(var i = 0, len = sn.length; i < len; i++){
11927                     if(sn[i] == node){
11928                         index = i;
11929                         break;
11930                     }
11931                 }
11932             }
11933             if(index != -1){
11934                 this.selNodes.splice(index, 1);
11935             }
11936             delete this.selMap[node.id];
11937             this.fireEvent("selectionchange", this, this.selNodes);
11938         }
11939     },
11940     
11941     /**
11942      * Clear all selections
11943      */
11944     clearSelections : function(suppressEvent){
11945         var sn = this.selNodes;
11946         if(sn.length > 0){
11947             for(var i = 0, len = sn.length; i < len; i++){
11948                 sn[i].ui.onSelectedChange(false);
11949             }
11950             this.selNodes = [];
11951             this.selMap = {};
11952             if(suppressEvent !== true){
11953                 this.fireEvent("selectionchange", this, this.selNodes);
11954             }
11955         }
11956     },
11957     
11958     /**
11959      * Returns true if the node is selected
11960      * @param {TreeNode} node The node to check
11961      * @return {Boolean}
11962      */
11963     isSelected : function(node){
11964         return this.selMap[node.id] ? true : false;  
11965     },
11966     
11967     /**
11968      * Returns an array of the selected nodes
11969      * @return {Array}
11970      */
11971     getSelectedNodes : function(){
11972         return this.selNodes;    
11973     },
11974
11975     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
11976
11977     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
11978
11979     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
11980 });/*
11981  * Based on:
11982  * Ext JS Library 1.1.1
11983  * Copyright(c) 2006-2007, Ext JS, LLC.
11984  *
11985  * Originally Released Under LGPL - original licence link has changed is not relivant.
11986  *
11987  * Fork - LGPL
11988  * <script type="text/javascript">
11989  */
11990  
11991 /**
11992  * @class Roo.tree.TreeNode
11993  * @extends Roo.data.Node
11994  * @cfg {String} text The text for this node
11995  * @cfg {Boolean} expanded true to start the node expanded
11996  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
11997  * @cfg {Boolean} allowDrop false if this node cannot be drop on
11998  * @cfg {Boolean} disabled true to start the node disabled
11999  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12000  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12001  * @cfg {String} cls A css class to be added to the node
12002  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12003  * @cfg {String} href URL of the link used for the node (defaults to #)
12004  * @cfg {String} hrefTarget target frame for the link
12005  * @cfg {String} qtip An Ext QuickTip for the node
12006  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12007  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12008  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12009  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12010  * (defaults to undefined with no checkbox rendered)
12011  * @constructor
12012  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12013  */
12014 Roo.tree.TreeNode = function(attributes){
12015     attributes = attributes || {};
12016     if(typeof attributes == "string"){
12017         attributes = {text: attributes};
12018     }
12019     this.childrenRendered = false;
12020     this.rendered = false;
12021     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12022     this.expanded = attributes.expanded === true;
12023     this.isTarget = attributes.isTarget !== false;
12024     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12025     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12026
12027     /**
12028      * Read-only. The text for this node. To change it use setText().
12029      * @type String
12030      */
12031     this.text = attributes.text;
12032     /**
12033      * True if this node is disabled.
12034      * @type Boolean
12035      */
12036     this.disabled = attributes.disabled === true;
12037
12038     this.addEvents({
12039         /**
12040         * @event textchange
12041         * Fires when the text for this node is changed
12042         * @param {Node} this This node
12043         * @param {String} text The new text
12044         * @param {String} oldText The old text
12045         */
12046         "textchange" : true,
12047         /**
12048         * @event beforeexpand
12049         * Fires before this node is expanded, return false to cancel.
12050         * @param {Node} this This node
12051         * @param {Boolean} deep
12052         * @param {Boolean} anim
12053         */
12054         "beforeexpand" : true,
12055         /**
12056         * @event beforecollapse
12057         * Fires before this node is collapsed, return false to cancel.
12058         * @param {Node} this This node
12059         * @param {Boolean} deep
12060         * @param {Boolean} anim
12061         */
12062         "beforecollapse" : true,
12063         /**
12064         * @event expand
12065         * Fires when this node is expanded
12066         * @param {Node} this This node
12067         */
12068         "expand" : true,
12069         /**
12070         * @event disabledchange
12071         * Fires when the disabled status of this node changes
12072         * @param {Node} this This node
12073         * @param {Boolean} disabled
12074         */
12075         "disabledchange" : true,
12076         /**
12077         * @event collapse
12078         * Fires when this node is collapsed
12079         * @param {Node} this This node
12080         */
12081         "collapse" : true,
12082         /**
12083         * @event beforeclick
12084         * Fires before click processing. Return false to cancel the default action.
12085         * @param {Node} this This node
12086         * @param {Roo.EventObject} e The event object
12087         */
12088         "beforeclick":true,
12089         /**
12090         * @event checkchange
12091         * Fires when a node with a checkbox's checked property changes
12092         * @param {Node} this This node
12093         * @param {Boolean} checked
12094         */
12095         "checkchange":true,
12096         /**
12097         * @event click
12098         * Fires when this node is clicked
12099         * @param {Node} this This node
12100         * @param {Roo.EventObject} e The event object
12101         */
12102         "click":true,
12103         /**
12104         * @event dblclick
12105         * Fires when this node is double clicked
12106         * @param {Node} this This node
12107         * @param {Roo.EventObject} e The event object
12108         */
12109         "dblclick":true,
12110         /**
12111         * @event contextmenu
12112         * Fires when this node is right clicked
12113         * @param {Node} this This node
12114         * @param {Roo.EventObject} e The event object
12115         */
12116         "contextmenu":true,
12117         /**
12118         * @event beforechildrenrendered
12119         * Fires right before the child nodes for this node are rendered
12120         * @param {Node} this This node
12121         */
12122         "beforechildrenrendered":true
12123     });
12124
12125     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12126
12127     /**
12128      * Read-only. The UI for this node
12129      * @type TreeNodeUI
12130      */
12131     this.ui = new uiClass(this);
12132     
12133     // finally support items[]
12134     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12135         return;
12136     }
12137     
12138     
12139     Roo.each(this.attributes.items, function(c) {
12140         this.appendChild(Roo.factory(c,Roo.Tree));
12141     }, this);
12142     delete this.attributes.items;
12143     
12144     
12145     
12146 };
12147 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12148     preventHScroll: true,
12149     /**
12150      * Returns true if this node is expanded
12151      * @return {Boolean}
12152      */
12153     isExpanded : function(){
12154         return this.expanded;
12155     },
12156
12157     /**
12158      * Returns the UI object for this node
12159      * @return {TreeNodeUI}
12160      */
12161     getUI : function(){
12162         return this.ui;
12163     },
12164
12165     // private override
12166     setFirstChild : function(node){
12167         var of = this.firstChild;
12168         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12169         if(this.childrenRendered && of && node != of){
12170             of.renderIndent(true, true);
12171         }
12172         if(this.rendered){
12173             this.renderIndent(true, true);
12174         }
12175     },
12176
12177     // private override
12178     setLastChild : function(node){
12179         var ol = this.lastChild;
12180         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12181         if(this.childrenRendered && ol && node != ol){
12182             ol.renderIndent(true, true);
12183         }
12184         if(this.rendered){
12185             this.renderIndent(true, true);
12186         }
12187     },
12188
12189     // these methods are overridden to provide lazy rendering support
12190     // private override
12191     appendChild : function()
12192     {
12193         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12194         if(node && this.childrenRendered){
12195             node.render();
12196         }
12197         this.ui.updateExpandIcon();
12198         return node;
12199     },
12200
12201     // private override
12202     removeChild : function(node){
12203         this.ownerTree.getSelectionModel().unselect(node);
12204         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12205         // if it's been rendered remove dom node
12206         if(this.childrenRendered){
12207             node.ui.remove();
12208         }
12209         if(this.childNodes.length < 1){
12210             this.collapse(false, false);
12211         }else{
12212             this.ui.updateExpandIcon();
12213         }
12214         if(!this.firstChild) {
12215             this.childrenRendered = false;
12216         }
12217         return node;
12218     },
12219
12220     // private override
12221     insertBefore : function(node, refNode){
12222         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12223         if(newNode && refNode && this.childrenRendered){
12224             node.render();
12225         }
12226         this.ui.updateExpandIcon();
12227         return newNode;
12228     },
12229
12230     /**
12231      * Sets the text for this node
12232      * @param {String} text
12233      */
12234     setText : function(text){
12235         var oldText = this.text;
12236         this.text = text;
12237         this.attributes.text = text;
12238         if(this.rendered){ // event without subscribing
12239             this.ui.onTextChange(this, text, oldText);
12240         }
12241         this.fireEvent("textchange", this, text, oldText);
12242     },
12243
12244     /**
12245      * Triggers selection of this node
12246      */
12247     select : function(){
12248         this.getOwnerTree().getSelectionModel().select(this);
12249     },
12250
12251     /**
12252      * Triggers deselection of this node
12253      */
12254     unselect : function(){
12255         this.getOwnerTree().getSelectionModel().unselect(this);
12256     },
12257
12258     /**
12259      * Returns true if this node is selected
12260      * @return {Boolean}
12261      */
12262     isSelected : function(){
12263         return this.getOwnerTree().getSelectionModel().isSelected(this);
12264     },
12265
12266     /**
12267      * Expand this node.
12268      * @param {Boolean} deep (optional) True to expand all children as well
12269      * @param {Boolean} anim (optional) false to cancel the default animation
12270      * @param {Function} callback (optional) A callback to be called when
12271      * expanding this node completes (does not wait for deep expand to complete).
12272      * Called with 1 parameter, this node.
12273      */
12274     expand : function(deep, anim, callback){
12275         if(!this.expanded){
12276             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12277                 return;
12278             }
12279             if(!this.childrenRendered){
12280                 this.renderChildren();
12281             }
12282             this.expanded = true;
12283             
12284             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12285                 this.ui.animExpand(function(){
12286                     this.fireEvent("expand", this);
12287                     if(typeof callback == "function"){
12288                         callback(this);
12289                     }
12290                     if(deep === true){
12291                         this.expandChildNodes(true);
12292                     }
12293                 }.createDelegate(this));
12294                 return;
12295             }else{
12296                 this.ui.expand();
12297                 this.fireEvent("expand", this);
12298                 if(typeof callback == "function"){
12299                     callback(this);
12300                 }
12301             }
12302         }else{
12303            if(typeof callback == "function"){
12304                callback(this);
12305            }
12306         }
12307         if(deep === true){
12308             this.expandChildNodes(true);
12309         }
12310     },
12311
12312     isHiddenRoot : function(){
12313         return this.isRoot && !this.getOwnerTree().rootVisible;
12314     },
12315
12316     /**
12317      * Collapse this node.
12318      * @param {Boolean} deep (optional) True to collapse all children as well
12319      * @param {Boolean} anim (optional) false to cancel the default animation
12320      */
12321     collapse : function(deep, anim){
12322         if(this.expanded && !this.isHiddenRoot()){
12323             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12324                 return;
12325             }
12326             this.expanded = false;
12327             if((this.getOwnerTree().animate && anim !== false) || anim){
12328                 this.ui.animCollapse(function(){
12329                     this.fireEvent("collapse", this);
12330                     if(deep === true){
12331                         this.collapseChildNodes(true);
12332                     }
12333                 }.createDelegate(this));
12334                 return;
12335             }else{
12336                 this.ui.collapse();
12337                 this.fireEvent("collapse", this);
12338             }
12339         }
12340         if(deep === true){
12341             var cs = this.childNodes;
12342             for(var i = 0, len = cs.length; i < len; i++) {
12343                 cs[i].collapse(true, false);
12344             }
12345         }
12346     },
12347
12348     // private
12349     delayedExpand : function(delay){
12350         if(!this.expandProcId){
12351             this.expandProcId = this.expand.defer(delay, this);
12352         }
12353     },
12354
12355     // private
12356     cancelExpand : function(){
12357         if(this.expandProcId){
12358             clearTimeout(this.expandProcId);
12359         }
12360         this.expandProcId = false;
12361     },
12362
12363     /**
12364      * Toggles expanded/collapsed state of the node
12365      */
12366     toggle : function(){
12367         if(this.expanded){
12368             this.collapse();
12369         }else{
12370             this.expand();
12371         }
12372     },
12373
12374     /**
12375      * Ensures all parent nodes are expanded
12376      */
12377     ensureVisible : function(callback){
12378         var tree = this.getOwnerTree();
12379         tree.expandPath(this.parentNode.getPath(), false, function(){
12380             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12381             Roo.callback(callback);
12382         }.createDelegate(this));
12383     },
12384
12385     /**
12386      * Expand all child nodes
12387      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12388      */
12389     expandChildNodes : function(deep){
12390         var cs = this.childNodes;
12391         for(var i = 0, len = cs.length; i < len; i++) {
12392                 cs[i].expand(deep);
12393         }
12394     },
12395
12396     /**
12397      * Collapse all child nodes
12398      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12399      */
12400     collapseChildNodes : function(deep){
12401         var cs = this.childNodes;
12402         for(var i = 0, len = cs.length; i < len; i++) {
12403                 cs[i].collapse(deep);
12404         }
12405     },
12406
12407     /**
12408      * Disables this node
12409      */
12410     disable : function(){
12411         this.disabled = true;
12412         this.unselect();
12413         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12414             this.ui.onDisableChange(this, true);
12415         }
12416         this.fireEvent("disabledchange", this, true);
12417     },
12418
12419     /**
12420      * Enables this node
12421      */
12422     enable : function(){
12423         this.disabled = false;
12424         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12425             this.ui.onDisableChange(this, false);
12426         }
12427         this.fireEvent("disabledchange", this, false);
12428     },
12429
12430     // private
12431     renderChildren : function(suppressEvent){
12432         if(suppressEvent !== false){
12433             this.fireEvent("beforechildrenrendered", this);
12434         }
12435         var cs = this.childNodes;
12436         for(var i = 0, len = cs.length; i < len; i++){
12437             cs[i].render(true);
12438         }
12439         this.childrenRendered = true;
12440     },
12441
12442     // private
12443     sort : function(fn, scope){
12444         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12445         if(this.childrenRendered){
12446             var cs = this.childNodes;
12447             for(var i = 0, len = cs.length; i < len; i++){
12448                 cs[i].render(true);
12449             }
12450         }
12451     },
12452
12453     // private
12454     render : function(bulkRender){
12455         this.ui.render(bulkRender);
12456         if(!this.rendered){
12457             this.rendered = true;
12458             if(this.expanded){
12459                 this.expanded = false;
12460                 this.expand(false, false);
12461             }
12462         }
12463     },
12464
12465     // private
12466     renderIndent : function(deep, refresh){
12467         if(refresh){
12468             this.ui.childIndent = null;
12469         }
12470         this.ui.renderIndent();
12471         if(deep === true && this.childrenRendered){
12472             var cs = this.childNodes;
12473             for(var i = 0, len = cs.length; i < len; i++){
12474                 cs[i].renderIndent(true, refresh);
12475             }
12476         }
12477     }
12478 });/*
12479  * Based on:
12480  * Ext JS Library 1.1.1
12481  * Copyright(c) 2006-2007, Ext JS, LLC.
12482  *
12483  * Originally Released Under LGPL - original licence link has changed is not relivant.
12484  *
12485  * Fork - LGPL
12486  * <script type="text/javascript">
12487  */
12488  
12489 /**
12490  * @class Roo.tree.AsyncTreeNode
12491  * @extends Roo.tree.TreeNode
12492  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12493  * @constructor
12494  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12495  */
12496  Roo.tree.AsyncTreeNode = function(config){
12497     this.loaded = false;
12498     this.loading = false;
12499     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12500     /**
12501     * @event beforeload
12502     * Fires before this node is loaded, return false to cancel
12503     * @param {Node} this This node
12504     */
12505     this.addEvents({'beforeload':true, 'load': true});
12506     /**
12507     * @event load
12508     * Fires when this node is loaded
12509     * @param {Node} this This node
12510     */
12511     /**
12512      * The loader used by this node (defaults to using the tree's defined loader)
12513      * @type TreeLoader
12514      * @property loader
12515      */
12516 };
12517 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12518     expand : function(deep, anim, callback){
12519         if(this.loading){ // if an async load is already running, waiting til it's done
12520             var timer;
12521             var f = function(){
12522                 if(!this.loading){ // done loading
12523                     clearInterval(timer);
12524                     this.expand(deep, anim, callback);
12525                 }
12526             }.createDelegate(this);
12527             timer = setInterval(f, 200);
12528             return;
12529         }
12530         if(!this.loaded){
12531             if(this.fireEvent("beforeload", this) === false){
12532                 return;
12533             }
12534             this.loading = true;
12535             this.ui.beforeLoad(this);
12536             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12537             if(loader){
12538                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12539                 return;
12540             }
12541         }
12542         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12543     },
12544     
12545     /**
12546      * Returns true if this node is currently loading
12547      * @return {Boolean}
12548      */
12549     isLoading : function(){
12550         return this.loading;  
12551     },
12552     
12553     loadComplete : function(deep, anim, callback){
12554         this.loading = false;
12555         this.loaded = true;
12556         this.ui.afterLoad(this);
12557         this.fireEvent("load", this);
12558         this.expand(deep, anim, callback);
12559     },
12560     
12561     /**
12562      * Returns true if this node has been loaded
12563      * @return {Boolean}
12564      */
12565     isLoaded : function(){
12566         return this.loaded;
12567     },
12568     
12569     hasChildNodes : function(){
12570         if(!this.isLeaf() && !this.loaded){
12571             return true;
12572         }else{
12573             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12574         }
12575     },
12576
12577     /**
12578      * Trigger a reload for this node
12579      * @param {Function} callback
12580      */
12581     reload : function(callback){
12582         this.collapse(false, false);
12583         while(this.firstChild){
12584             this.removeChild(this.firstChild);
12585         }
12586         this.childrenRendered = false;
12587         this.loaded = false;
12588         if(this.isHiddenRoot()){
12589             this.expanded = false;
12590         }
12591         this.expand(false, false, callback);
12592     }
12593 });/*
12594  * Based on:
12595  * Ext JS Library 1.1.1
12596  * Copyright(c) 2006-2007, Ext JS, LLC.
12597  *
12598  * Originally Released Under LGPL - original licence link has changed is not relivant.
12599  *
12600  * Fork - LGPL
12601  * <script type="text/javascript">
12602  */
12603  
12604 /**
12605  * @class Roo.tree.TreeNodeUI
12606  * @constructor
12607  * @param {Object} node The node to render
12608  * The TreeNode UI implementation is separate from the
12609  * tree implementation. Unless you are customizing the tree UI,
12610  * you should never have to use this directly.
12611  */
12612 Roo.tree.TreeNodeUI = function(node){
12613     this.node = node;
12614     this.rendered = false;
12615     this.animating = false;
12616     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12617 };
12618
12619 Roo.tree.TreeNodeUI.prototype = {
12620     removeChild : function(node){
12621         if(this.rendered){
12622             this.ctNode.removeChild(node.ui.getEl());
12623         }
12624     },
12625
12626     beforeLoad : function(){
12627          this.addClass("x-tree-node-loading");
12628     },
12629
12630     afterLoad : function(){
12631          this.removeClass("x-tree-node-loading");
12632     },
12633
12634     onTextChange : function(node, text, oldText){
12635         if(this.rendered){
12636             this.textNode.innerHTML = text;
12637         }
12638     },
12639
12640     onDisableChange : function(node, state){
12641         this.disabled = state;
12642         if(state){
12643             this.addClass("x-tree-node-disabled");
12644         }else{
12645             this.removeClass("x-tree-node-disabled");
12646         }
12647     },
12648
12649     onSelectedChange : function(state){
12650         if(state){
12651             this.focus();
12652             this.addClass("x-tree-selected");
12653         }else{
12654             //this.blur();
12655             this.removeClass("x-tree-selected");
12656         }
12657     },
12658
12659     onMove : function(tree, node, oldParent, newParent, index, refNode){
12660         this.childIndent = null;
12661         if(this.rendered){
12662             var targetNode = newParent.ui.getContainer();
12663             if(!targetNode){//target not rendered
12664                 this.holder = document.createElement("div");
12665                 this.holder.appendChild(this.wrap);
12666                 return;
12667             }
12668             var insertBefore = refNode ? refNode.ui.getEl() : null;
12669             if(insertBefore){
12670                 targetNode.insertBefore(this.wrap, insertBefore);
12671             }else{
12672                 targetNode.appendChild(this.wrap);
12673             }
12674             this.node.renderIndent(true);
12675         }
12676     },
12677
12678     addClass : function(cls){
12679         if(this.elNode){
12680             Roo.fly(this.elNode).addClass(cls);
12681         }
12682     },
12683
12684     removeClass : function(cls){
12685         if(this.elNode){
12686             Roo.fly(this.elNode).removeClass(cls);
12687         }
12688     },
12689
12690     remove : function(){
12691         if(this.rendered){
12692             this.holder = document.createElement("div");
12693             this.holder.appendChild(this.wrap);
12694         }
12695     },
12696
12697     fireEvent : function(){
12698         return this.node.fireEvent.apply(this.node, arguments);
12699     },
12700
12701     initEvents : function(){
12702         this.node.on("move", this.onMove, this);
12703         var E = Roo.EventManager;
12704         var a = this.anchor;
12705
12706         var el = Roo.fly(a, '_treeui');
12707
12708         if(Roo.isOpera){ // opera render bug ignores the CSS
12709             el.setStyle("text-decoration", "none");
12710         }
12711
12712         el.on("click", this.onClick, this);
12713         el.on("dblclick", this.onDblClick, this);
12714
12715         if(this.checkbox){
12716             Roo.EventManager.on(this.checkbox,
12717                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12718         }
12719
12720         el.on("contextmenu", this.onContextMenu, this);
12721
12722         var icon = Roo.fly(this.iconNode);
12723         icon.on("click", this.onClick, this);
12724         icon.on("dblclick", this.onDblClick, this);
12725         icon.on("contextmenu", this.onContextMenu, this);
12726         E.on(this.ecNode, "click", this.ecClick, this, true);
12727
12728         if(this.node.disabled){
12729             this.addClass("x-tree-node-disabled");
12730         }
12731         if(this.node.hidden){
12732             this.addClass("x-tree-node-disabled");
12733         }
12734         var ot = this.node.getOwnerTree();
12735         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12736         if(dd && (!this.node.isRoot || ot.rootVisible)){
12737             Roo.dd.Registry.register(this.elNode, {
12738                 node: this.node,
12739                 handles: this.getDDHandles(),
12740                 isHandle: false
12741             });
12742         }
12743     },
12744
12745     getDDHandles : function(){
12746         return [this.iconNode, this.textNode];
12747     },
12748
12749     hide : function(){
12750         if(this.rendered){
12751             this.wrap.style.display = "none";
12752         }
12753     },
12754
12755     show : function(){
12756         if(this.rendered){
12757             this.wrap.style.display = "";
12758         }
12759     },
12760
12761     onContextMenu : function(e){
12762         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12763             e.preventDefault();
12764             this.focus();
12765             this.fireEvent("contextmenu", this.node, e);
12766         }
12767     },
12768
12769     onClick : function(e){
12770         if(this.dropping){
12771             e.stopEvent();
12772             return;
12773         }
12774         if(this.fireEvent("beforeclick", this.node, e) !== false){
12775             if(!this.disabled && this.node.attributes.href){
12776                 this.fireEvent("click", this.node, e);
12777                 return;
12778             }
12779             e.preventDefault();
12780             if(this.disabled){
12781                 return;
12782             }
12783
12784             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12785                 this.node.toggle();
12786             }
12787
12788             this.fireEvent("click", this.node, e);
12789         }else{
12790             e.stopEvent();
12791         }
12792     },
12793
12794     onDblClick : function(e){
12795         e.preventDefault();
12796         if(this.disabled){
12797             return;
12798         }
12799         if(this.checkbox){
12800             this.toggleCheck();
12801         }
12802         if(!this.animating && this.node.hasChildNodes()){
12803             this.node.toggle();
12804         }
12805         this.fireEvent("dblclick", this.node, e);
12806     },
12807
12808     onCheckChange : function(){
12809         var checked = this.checkbox.checked;
12810         this.node.attributes.checked = checked;
12811         this.fireEvent('checkchange', this.node, checked);
12812     },
12813
12814     ecClick : function(e){
12815         if(!this.animating && this.node.hasChildNodes()){
12816             this.node.toggle();
12817         }
12818     },
12819
12820     startDrop : function(){
12821         this.dropping = true;
12822     },
12823
12824     // delayed drop so the click event doesn't get fired on a drop
12825     endDrop : function(){
12826        setTimeout(function(){
12827            this.dropping = false;
12828        }.createDelegate(this), 50);
12829     },
12830
12831     expand : function(){
12832         this.updateExpandIcon();
12833         this.ctNode.style.display = "";
12834     },
12835
12836     focus : function(){
12837         if(!this.node.preventHScroll){
12838             try{this.anchor.focus();
12839             }catch(e){}
12840         }else if(!Roo.isIE){
12841             try{
12842                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12843                 var l = noscroll.scrollLeft;
12844                 this.anchor.focus();
12845                 noscroll.scrollLeft = l;
12846             }catch(e){}
12847         }
12848     },
12849
12850     toggleCheck : function(value){
12851         var cb = this.checkbox;
12852         if(cb){
12853             cb.checked = (value === undefined ? !cb.checked : value);
12854         }
12855     },
12856
12857     blur : function(){
12858         try{
12859             this.anchor.blur();
12860         }catch(e){}
12861     },
12862
12863     animExpand : function(callback){
12864         var ct = Roo.get(this.ctNode);
12865         ct.stopFx();
12866         if(!this.node.hasChildNodes()){
12867             this.updateExpandIcon();
12868             this.ctNode.style.display = "";
12869             Roo.callback(callback);
12870             return;
12871         }
12872         this.animating = true;
12873         this.updateExpandIcon();
12874
12875         ct.slideIn('t', {
12876            callback : function(){
12877                this.animating = false;
12878                Roo.callback(callback);
12879             },
12880             scope: this,
12881             duration: this.node.ownerTree.duration || .25
12882         });
12883     },
12884
12885     highlight : function(){
12886         var tree = this.node.getOwnerTree();
12887         Roo.fly(this.wrap).highlight(
12888             tree.hlColor || "C3DAF9",
12889             {endColor: tree.hlBaseColor}
12890         );
12891     },
12892
12893     collapse : function(){
12894         this.updateExpandIcon();
12895         this.ctNode.style.display = "none";
12896     },
12897
12898     animCollapse : function(callback){
12899         var ct = Roo.get(this.ctNode);
12900         ct.enableDisplayMode('block');
12901         ct.stopFx();
12902
12903         this.animating = true;
12904         this.updateExpandIcon();
12905
12906         ct.slideOut('t', {
12907             callback : function(){
12908                this.animating = false;
12909                Roo.callback(callback);
12910             },
12911             scope: this,
12912             duration: this.node.ownerTree.duration || .25
12913         });
12914     },
12915
12916     getContainer : function(){
12917         return this.ctNode;
12918     },
12919
12920     getEl : function(){
12921         return this.wrap;
12922     },
12923
12924     appendDDGhost : function(ghostNode){
12925         ghostNode.appendChild(this.elNode.cloneNode(true));
12926     },
12927
12928     getDDRepairXY : function(){
12929         return Roo.lib.Dom.getXY(this.iconNode);
12930     },
12931
12932     onRender : function(){
12933         this.render();
12934     },
12935
12936     render : function(bulkRender){
12937         var n = this.node, a = n.attributes;
12938         var targetNode = n.parentNode ?
12939               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
12940
12941         if(!this.rendered){
12942             this.rendered = true;
12943
12944             this.renderElements(n, a, targetNode, bulkRender);
12945
12946             if(a.qtip){
12947                if(this.textNode.setAttributeNS){
12948                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
12949                    if(a.qtipTitle){
12950                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
12951                    }
12952                }else{
12953                    this.textNode.setAttribute("ext:qtip", a.qtip);
12954                    if(a.qtipTitle){
12955                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
12956                    }
12957                }
12958             }else if(a.qtipCfg){
12959                 a.qtipCfg.target = Roo.id(this.textNode);
12960                 Roo.QuickTips.register(a.qtipCfg);
12961             }
12962             this.initEvents();
12963             if(!this.node.expanded){
12964                 this.updateExpandIcon();
12965             }
12966         }else{
12967             if(bulkRender === true) {
12968                 targetNode.appendChild(this.wrap);
12969             }
12970         }
12971     },
12972
12973     renderElements : function(n, a, targetNode, bulkRender)
12974     {
12975         // add some indent caching, this helps performance when rendering a large tree
12976         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
12977         var t = n.getOwnerTree();
12978         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
12979         if (typeof(n.attributes.html) != 'undefined') {
12980             txt = n.attributes.html;
12981         }
12982         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
12983         var cb = typeof a.checked == 'boolean';
12984         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
12985         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
12986             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
12987             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
12988             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
12989             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
12990             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
12991              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
12992                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
12993             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
12994             "</li>"];
12995
12996         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
12997             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
12998                                 n.nextSibling.ui.getEl(), buf.join(""));
12999         }else{
13000             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13001         }
13002
13003         this.elNode = this.wrap.childNodes[0];
13004         this.ctNode = this.wrap.childNodes[1];
13005         var cs = this.elNode.childNodes;
13006         this.indentNode = cs[0];
13007         this.ecNode = cs[1];
13008         this.iconNode = cs[2];
13009         var index = 3;
13010         if(cb){
13011             this.checkbox = cs[3];
13012             index++;
13013         }
13014         this.anchor = cs[index];
13015         this.textNode = cs[index].firstChild;
13016     },
13017
13018     getAnchor : function(){
13019         return this.anchor;
13020     },
13021
13022     getTextEl : function(){
13023         return this.textNode;
13024     },
13025
13026     getIconEl : function(){
13027         return this.iconNode;
13028     },
13029
13030     isChecked : function(){
13031         return this.checkbox ? this.checkbox.checked : false;
13032     },
13033
13034     updateExpandIcon : function(){
13035         if(this.rendered){
13036             var n = this.node, c1, c2;
13037             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13038             var hasChild = n.hasChildNodes();
13039             if(hasChild){
13040                 if(n.expanded){
13041                     cls += "-minus";
13042                     c1 = "x-tree-node-collapsed";
13043                     c2 = "x-tree-node-expanded";
13044                 }else{
13045                     cls += "-plus";
13046                     c1 = "x-tree-node-expanded";
13047                     c2 = "x-tree-node-collapsed";
13048                 }
13049                 if(this.wasLeaf){
13050                     this.removeClass("x-tree-node-leaf");
13051                     this.wasLeaf = false;
13052                 }
13053                 if(this.c1 != c1 || this.c2 != c2){
13054                     Roo.fly(this.elNode).replaceClass(c1, c2);
13055                     this.c1 = c1; this.c2 = c2;
13056                 }
13057             }else{
13058                 // this changes non-leafs into leafs if they have no children.
13059                 // it's not very rational behaviour..
13060                 
13061                 if(!this.wasLeaf && this.node.leaf){
13062                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13063                     delete this.c1;
13064                     delete this.c2;
13065                     this.wasLeaf = true;
13066                 }
13067             }
13068             var ecc = "x-tree-ec-icon "+cls;
13069             if(this.ecc != ecc){
13070                 this.ecNode.className = ecc;
13071                 this.ecc = ecc;
13072             }
13073         }
13074     },
13075
13076     getChildIndent : function(){
13077         if(!this.childIndent){
13078             var buf = [];
13079             var p = this.node;
13080             while(p){
13081                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13082                     if(!p.isLast()) {
13083                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13084                     } else {
13085                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13086                     }
13087                 }
13088                 p = p.parentNode;
13089             }
13090             this.childIndent = buf.join("");
13091         }
13092         return this.childIndent;
13093     },
13094
13095     renderIndent : function(){
13096         if(this.rendered){
13097             var indent = "";
13098             var p = this.node.parentNode;
13099             if(p){
13100                 indent = p.ui.getChildIndent();
13101             }
13102             if(this.indentMarkup != indent){ // don't rerender if not required
13103                 this.indentNode.innerHTML = indent;
13104                 this.indentMarkup = indent;
13105             }
13106             this.updateExpandIcon();
13107         }
13108     }
13109 };
13110
13111 Roo.tree.RootTreeNodeUI = function(){
13112     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13113 };
13114 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13115     render : function(){
13116         if(!this.rendered){
13117             var targetNode = this.node.ownerTree.innerCt.dom;
13118             this.node.expanded = true;
13119             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13120             this.wrap = this.ctNode = targetNode.firstChild;
13121         }
13122     },
13123     collapse : function(){
13124     },
13125     expand : function(){
13126     }
13127 });/*
13128  * Based on:
13129  * Ext JS Library 1.1.1
13130  * Copyright(c) 2006-2007, Ext JS, LLC.
13131  *
13132  * Originally Released Under LGPL - original licence link has changed is not relivant.
13133  *
13134  * Fork - LGPL
13135  * <script type="text/javascript">
13136  */
13137 /**
13138  * @class Roo.tree.TreeLoader
13139  * @extends Roo.util.Observable
13140  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13141  * nodes from a specified URL. The response must be a javascript Array definition
13142  * who's elements are node definition objects. eg:
13143  * <pre><code>
13144 {  success : true,
13145    data :      [
13146    
13147     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13148     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13149     ]
13150 }
13151
13152
13153 </code></pre>
13154  * <br><br>
13155  * The old style respose with just an array is still supported, but not recommended.
13156  * <br><br>
13157  *
13158  * A server request is sent, and child nodes are loaded only when a node is expanded.
13159  * The loading node's id is passed to the server under the parameter name "node" to
13160  * enable the server to produce the correct child nodes.
13161  * <br><br>
13162  * To pass extra parameters, an event handler may be attached to the "beforeload"
13163  * event, and the parameters specified in the TreeLoader's baseParams property:
13164  * <pre><code>
13165     myTreeLoader.on("beforeload", function(treeLoader, node) {
13166         this.baseParams.category = node.attributes.category;
13167     }, this);
13168     
13169 </code></pre>
13170  *
13171  * This would pass an HTTP parameter called "category" to the server containing
13172  * the value of the Node's "category" attribute.
13173  * @constructor
13174  * Creates a new Treeloader.
13175  * @param {Object} config A config object containing config properties.
13176  */
13177 Roo.tree.TreeLoader = function(config){
13178     this.baseParams = {};
13179     this.requestMethod = "POST";
13180     Roo.apply(this, config);
13181
13182     this.addEvents({
13183     
13184         /**
13185          * @event beforeload
13186          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13187          * @param {Object} This TreeLoader object.
13188          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13189          * @param {Object} callback The callback function specified in the {@link #load} call.
13190          */
13191         beforeload : true,
13192         /**
13193          * @event load
13194          * Fires when the node has been successfuly loaded.
13195          * @param {Object} This TreeLoader object.
13196          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13197          * @param {Object} response The response object containing the data from the server.
13198          */
13199         load : true,
13200         /**
13201          * @event loadexception
13202          * Fires if the network request failed.
13203          * @param {Object} This TreeLoader object.
13204          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13205          * @param {Object} response The response object containing the data from the server.
13206          */
13207         loadexception : true,
13208         /**
13209          * @event create
13210          * Fires before a node is created, enabling you to return custom Node types 
13211          * @param {Object} This TreeLoader object.
13212          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13213          */
13214         create : true
13215     });
13216
13217     Roo.tree.TreeLoader.superclass.constructor.call(this);
13218 };
13219
13220 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13221     /**
13222     * @cfg {String} dataUrl The URL from which to request a Json string which
13223     * specifies an array of node definition object representing the child nodes
13224     * to be loaded.
13225     */
13226     /**
13227     * @cfg {String} requestMethod either GET or POST
13228     * defaults to POST (due to BC)
13229     * to be loaded.
13230     */
13231     /**
13232     * @cfg {Object} baseParams (optional) An object containing properties which
13233     * specify HTTP parameters to be passed to each request for child nodes.
13234     */
13235     /**
13236     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13237     * created by this loader. If the attributes sent by the server have an attribute in this object,
13238     * they take priority.
13239     */
13240     /**
13241     * @cfg {Object} uiProviders (optional) An object containing properties which
13242     * 
13243     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13244     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13245     * <i>uiProvider</i> attribute of a returned child node is a string rather
13246     * than a reference to a TreeNodeUI implementation, this that string value
13247     * is used as a property name in the uiProviders object. You can define the provider named
13248     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13249     */
13250     uiProviders : {},
13251
13252     /**
13253     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13254     * child nodes before loading.
13255     */
13256     clearOnLoad : true,
13257
13258     /**
13259     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13260     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13261     * Grid query { data : [ .....] }
13262     */
13263     
13264     root : false,
13265      /**
13266     * @cfg {String} queryParam (optional) 
13267     * Name of the query as it will be passed on the querystring (defaults to 'node')
13268     * eg. the request will be ?node=[id]
13269     */
13270     
13271     
13272     queryParam: false,
13273     
13274     /**
13275      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13276      * This is called automatically when a node is expanded, but may be used to reload
13277      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13278      * @param {Roo.tree.TreeNode} node
13279      * @param {Function} callback
13280      */
13281     load : function(node, callback){
13282         if(this.clearOnLoad){
13283             while(node.firstChild){
13284                 node.removeChild(node.firstChild);
13285             }
13286         }
13287         if(node.attributes.children){ // preloaded json children
13288             var cs = node.attributes.children;
13289             for(var i = 0, len = cs.length; i < len; i++){
13290                 node.appendChild(this.createNode(cs[i]));
13291             }
13292             if(typeof callback == "function"){
13293                 callback();
13294             }
13295         }else if(this.dataUrl){
13296             this.requestData(node, callback);
13297         }
13298     },
13299
13300     getParams: function(node){
13301         var buf = [], bp = this.baseParams;
13302         for(var key in bp){
13303             if(typeof bp[key] != "function"){
13304                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13305             }
13306         }
13307         var n = this.queryParam === false ? 'node' : this.queryParam;
13308         buf.push(n + "=", encodeURIComponent(node.id));
13309         return buf.join("");
13310     },
13311
13312     requestData : function(node, callback){
13313         if(this.fireEvent("beforeload", this, node, callback) !== false){
13314             this.transId = Roo.Ajax.request({
13315                 method:this.requestMethod,
13316                 url: this.dataUrl||this.url,
13317                 success: this.handleResponse,
13318                 failure: this.handleFailure,
13319                 scope: this,
13320                 argument: {callback: callback, node: node},
13321                 params: this.getParams(node)
13322             });
13323         }else{
13324             // if the load is cancelled, make sure we notify
13325             // the node that we are done
13326             if(typeof callback == "function"){
13327                 callback();
13328             }
13329         }
13330     },
13331
13332     isLoading : function(){
13333         return this.transId ? true : false;
13334     },
13335
13336     abort : function(){
13337         if(this.isLoading()){
13338             Roo.Ajax.abort(this.transId);
13339         }
13340     },
13341
13342     // private
13343     createNode : function(attr)
13344     {
13345         // apply baseAttrs, nice idea Corey!
13346         if(this.baseAttrs){
13347             Roo.applyIf(attr, this.baseAttrs);
13348         }
13349         if(this.applyLoader !== false){
13350             attr.loader = this;
13351         }
13352         // uiProvider = depreciated..
13353         
13354         if(typeof(attr.uiProvider) == 'string'){
13355            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13356                 /**  eval:var:attr */ eval(attr.uiProvider);
13357         }
13358         if(typeof(this.uiProviders['default']) != 'undefined') {
13359             attr.uiProvider = this.uiProviders['default'];
13360         }
13361         
13362         this.fireEvent('create', this, attr);
13363         
13364         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13365         return(attr.leaf ?
13366                         new Roo.tree.TreeNode(attr) :
13367                         new Roo.tree.AsyncTreeNode(attr));
13368     },
13369
13370     processResponse : function(response, node, callback)
13371     {
13372         var json = response.responseText;
13373         try {
13374             
13375             var o = Roo.decode(json);
13376             
13377             if (this.root === false && typeof(o.success) != undefined) {
13378                 this.root = 'data'; // the default behaviour for list like data..
13379                 }
13380                 
13381             if (this.root !== false &&  !o.success) {
13382                 // it's a failure condition.
13383                 var a = response.argument;
13384                 this.fireEvent("loadexception", this, a.node, response);
13385                 Roo.log("Load failed - should have a handler really");
13386                 return;
13387             }
13388             
13389             
13390             
13391             if (this.root !== false) {
13392                  o = o[this.root];
13393             }
13394             
13395             for(var i = 0, len = o.length; i < len; i++){
13396                 var n = this.createNode(o[i]);
13397                 if(n){
13398                     node.appendChild(n);
13399                 }
13400             }
13401             if(typeof callback == "function"){
13402                 callback(this, node);
13403             }
13404         }catch(e){
13405             this.handleFailure(response);
13406         }
13407     },
13408
13409     handleResponse : function(response){
13410         this.transId = false;
13411         var a = response.argument;
13412         this.processResponse(response, a.node, a.callback);
13413         this.fireEvent("load", this, a.node, response);
13414     },
13415
13416     handleFailure : function(response)
13417     {
13418         // should handle failure better..
13419         this.transId = false;
13420         var a = response.argument;
13421         this.fireEvent("loadexception", this, a.node, response);
13422         if(typeof a.callback == "function"){
13423             a.callback(this, a.node);
13424         }
13425     }
13426 });/*
13427  * Based on:
13428  * Ext JS Library 1.1.1
13429  * Copyright(c) 2006-2007, Ext JS, LLC.
13430  *
13431  * Originally Released Under LGPL - original licence link has changed is not relivant.
13432  *
13433  * Fork - LGPL
13434  * <script type="text/javascript">
13435  */
13436
13437 /**
13438 * @class Roo.tree.TreeFilter
13439 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13440 * @param {TreePanel} tree
13441 * @param {Object} config (optional)
13442  */
13443 Roo.tree.TreeFilter = function(tree, config){
13444     this.tree = tree;
13445     this.filtered = {};
13446     Roo.apply(this, config);
13447 };
13448
13449 Roo.tree.TreeFilter.prototype = {
13450     clearBlank:false,
13451     reverse:false,
13452     autoClear:false,
13453     remove:false,
13454
13455      /**
13456      * Filter the data by a specific attribute.
13457      * @param {String/RegExp} value Either string that the attribute value
13458      * should start with or a RegExp to test against the attribute
13459      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13460      * @param {TreeNode} startNode (optional) The node to start the filter at.
13461      */
13462     filter : function(value, attr, startNode){
13463         attr = attr || "text";
13464         var f;
13465         if(typeof value == "string"){
13466             var vlen = value.length;
13467             // auto clear empty filter
13468             if(vlen == 0 && this.clearBlank){
13469                 this.clear();
13470                 return;
13471             }
13472             value = value.toLowerCase();
13473             f = function(n){
13474                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13475             };
13476         }else if(value.exec){ // regex?
13477             f = function(n){
13478                 return value.test(n.attributes[attr]);
13479             };
13480         }else{
13481             throw 'Illegal filter type, must be string or regex';
13482         }
13483         this.filterBy(f, null, startNode);
13484         },
13485
13486     /**
13487      * Filter by a function. The passed function will be called with each
13488      * node in the tree (or from the startNode). If the function returns true, the node is kept
13489      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13490      * @param {Function} fn The filter function
13491      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13492      */
13493     filterBy : function(fn, scope, startNode){
13494         startNode = startNode || this.tree.root;
13495         if(this.autoClear){
13496             this.clear();
13497         }
13498         var af = this.filtered, rv = this.reverse;
13499         var f = function(n){
13500             if(n == startNode){
13501                 return true;
13502             }
13503             if(af[n.id]){
13504                 return false;
13505             }
13506             var m = fn.call(scope || n, n);
13507             if(!m || rv){
13508                 af[n.id] = n;
13509                 n.ui.hide();
13510                 return false;
13511             }
13512             return true;
13513         };
13514         startNode.cascade(f);
13515         if(this.remove){
13516            for(var id in af){
13517                if(typeof id != "function"){
13518                    var n = af[id];
13519                    if(n && n.parentNode){
13520                        n.parentNode.removeChild(n);
13521                    }
13522                }
13523            }
13524         }
13525     },
13526
13527     /**
13528      * Clears the current filter. Note: with the "remove" option
13529      * set a filter cannot be cleared.
13530      */
13531     clear : function(){
13532         var t = this.tree;
13533         var af = this.filtered;
13534         for(var id in af){
13535             if(typeof id != "function"){
13536                 var n = af[id];
13537                 if(n){
13538                     n.ui.show();
13539                 }
13540             }
13541         }
13542         this.filtered = {};
13543     }
13544 };
13545 /*
13546  * Based on:
13547  * Ext JS Library 1.1.1
13548  * Copyright(c) 2006-2007, Ext JS, LLC.
13549  *
13550  * Originally Released Under LGPL - original licence link has changed is not relivant.
13551  *
13552  * Fork - LGPL
13553  * <script type="text/javascript">
13554  */
13555  
13556
13557 /**
13558  * @class Roo.tree.TreeSorter
13559  * Provides sorting of nodes in a TreePanel
13560  * 
13561  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13562  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13563  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13564  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13565  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13566  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13567  * @constructor
13568  * @param {TreePanel} tree
13569  * @param {Object} config
13570  */
13571 Roo.tree.TreeSorter = function(tree, config){
13572     Roo.apply(this, config);
13573     tree.on("beforechildrenrendered", this.doSort, this);
13574     tree.on("append", this.updateSort, this);
13575     tree.on("insert", this.updateSort, this);
13576     
13577     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13578     var p = this.property || "text";
13579     var sortType = this.sortType;
13580     var fs = this.folderSort;
13581     var cs = this.caseSensitive === true;
13582     var leafAttr = this.leafAttr || 'leaf';
13583
13584     this.sortFn = function(n1, n2){
13585         if(fs){
13586             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13587                 return 1;
13588             }
13589             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13590                 return -1;
13591             }
13592         }
13593         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13594         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13595         if(v1 < v2){
13596                         return dsc ? +1 : -1;
13597                 }else if(v1 > v2){
13598                         return dsc ? -1 : +1;
13599         }else{
13600                 return 0;
13601         }
13602     };
13603 };
13604
13605 Roo.tree.TreeSorter.prototype = {
13606     doSort : function(node){
13607         node.sort(this.sortFn);
13608     },
13609     
13610     compareNodes : function(n1, n2){
13611         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13612     },
13613     
13614     updateSort : function(tree, node){
13615         if(node.childrenRendered){
13616             this.doSort.defer(1, this, [node]);
13617         }
13618     }
13619 };/*
13620  * Based on:
13621  * Ext JS Library 1.1.1
13622  * Copyright(c) 2006-2007, Ext JS, LLC.
13623  *
13624  * Originally Released Under LGPL - original licence link has changed is not relivant.
13625  *
13626  * Fork - LGPL
13627  * <script type="text/javascript">
13628  */
13629
13630 if(Roo.dd.DropZone){
13631     
13632 Roo.tree.TreeDropZone = function(tree, config){
13633     this.allowParentInsert = false;
13634     this.allowContainerDrop = false;
13635     this.appendOnly = false;
13636     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13637     this.tree = tree;
13638     this.lastInsertClass = "x-tree-no-status";
13639     this.dragOverData = {};
13640 };
13641
13642 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13643     ddGroup : "TreeDD",
13644     scroll:  true,
13645     
13646     expandDelay : 1000,
13647     
13648     expandNode : function(node){
13649         if(node.hasChildNodes() && !node.isExpanded()){
13650             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13651         }
13652     },
13653     
13654     queueExpand : function(node){
13655         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13656     },
13657     
13658     cancelExpand : function(){
13659         if(this.expandProcId){
13660             clearTimeout(this.expandProcId);
13661             this.expandProcId = false;
13662         }
13663     },
13664     
13665     isValidDropPoint : function(n, pt, dd, e, data){
13666         if(!n || !data){ return false; }
13667         var targetNode = n.node;
13668         var dropNode = data.node;
13669         // default drop rules
13670         if(!(targetNode && targetNode.isTarget && pt)){
13671             return false;
13672         }
13673         if(pt == "append" && targetNode.allowChildren === false){
13674             return false;
13675         }
13676         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13677             return false;
13678         }
13679         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13680             return false;
13681         }
13682         // reuse the object
13683         var overEvent = this.dragOverData;
13684         overEvent.tree = this.tree;
13685         overEvent.target = targetNode;
13686         overEvent.data = data;
13687         overEvent.point = pt;
13688         overEvent.source = dd;
13689         overEvent.rawEvent = e;
13690         overEvent.dropNode = dropNode;
13691         overEvent.cancel = false;  
13692         var result = this.tree.fireEvent("nodedragover", overEvent);
13693         return overEvent.cancel === false && result !== false;
13694     },
13695     
13696     getDropPoint : function(e, n, dd)
13697     {
13698         var tn = n.node;
13699         if(tn.isRoot){
13700             return tn.allowChildren !== false ? "append" : false; // always append for root
13701         }
13702         var dragEl = n.ddel;
13703         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13704         var y = Roo.lib.Event.getPageY(e);
13705         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13706         
13707         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13708         var noAppend = tn.allowChildren === false;
13709         if(this.appendOnly || tn.parentNode.allowChildren === false){
13710             return noAppend ? false : "append";
13711         }
13712         var noBelow = false;
13713         if(!this.allowParentInsert){
13714             noBelow = tn.hasChildNodes() && tn.isExpanded();
13715         }
13716         var q = (b - t) / (noAppend ? 2 : 3);
13717         if(y >= t && y < (t + q)){
13718             return "above";
13719         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13720             return "below";
13721         }else{
13722             return "append";
13723         }
13724     },
13725     
13726     onNodeEnter : function(n, dd, e, data)
13727     {
13728         this.cancelExpand();
13729     },
13730     
13731     onNodeOver : function(n, dd, e, data)
13732     {
13733        
13734         var pt = this.getDropPoint(e, n, dd);
13735         var node = n.node;
13736         
13737         // auto node expand check
13738         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13739             this.queueExpand(node);
13740         }else if(pt != "append"){
13741             this.cancelExpand();
13742         }
13743         
13744         // set the insert point style on the target node
13745         var returnCls = this.dropNotAllowed;
13746         if(this.isValidDropPoint(n, pt, dd, e, data)){
13747            if(pt){
13748                var el = n.ddel;
13749                var cls;
13750                if(pt == "above"){
13751                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13752                    cls = "x-tree-drag-insert-above";
13753                }else if(pt == "below"){
13754                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13755                    cls = "x-tree-drag-insert-below";
13756                }else{
13757                    returnCls = "x-tree-drop-ok-append";
13758                    cls = "x-tree-drag-append";
13759                }
13760                if(this.lastInsertClass != cls){
13761                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13762                    this.lastInsertClass = cls;
13763                }
13764            }
13765        }
13766        return returnCls;
13767     },
13768     
13769     onNodeOut : function(n, dd, e, data){
13770         
13771         this.cancelExpand();
13772         this.removeDropIndicators(n);
13773     },
13774     
13775     onNodeDrop : function(n, dd, e, data){
13776         var point = this.getDropPoint(e, n, dd);
13777         var targetNode = n.node;
13778         targetNode.ui.startDrop();
13779         if(!this.isValidDropPoint(n, point, dd, e, data)){
13780             targetNode.ui.endDrop();
13781             return false;
13782         }
13783         // first try to find the drop node
13784         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13785         var dropEvent = {
13786             tree : this.tree,
13787             target: targetNode,
13788             data: data,
13789             point: point,
13790             source: dd,
13791             rawEvent: e,
13792             dropNode: dropNode,
13793             cancel: !dropNode   
13794         };
13795         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13796         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13797             targetNode.ui.endDrop();
13798             return false;
13799         }
13800         // allow target changing
13801         targetNode = dropEvent.target;
13802         if(point == "append" && !targetNode.isExpanded()){
13803             targetNode.expand(false, null, function(){
13804                 this.completeDrop(dropEvent);
13805             }.createDelegate(this));
13806         }else{
13807             this.completeDrop(dropEvent);
13808         }
13809         return true;
13810     },
13811     
13812     completeDrop : function(de){
13813         var ns = de.dropNode, p = de.point, t = de.target;
13814         if(!(ns instanceof Array)){
13815             ns = [ns];
13816         }
13817         var n;
13818         for(var i = 0, len = ns.length; i < len; i++){
13819             n = ns[i];
13820             if(p == "above"){
13821                 t.parentNode.insertBefore(n, t);
13822             }else if(p == "below"){
13823                 t.parentNode.insertBefore(n, t.nextSibling);
13824             }else{
13825                 t.appendChild(n);
13826             }
13827         }
13828         n.ui.focus();
13829         if(this.tree.hlDrop){
13830             n.ui.highlight();
13831         }
13832         t.ui.endDrop();
13833         this.tree.fireEvent("nodedrop", de);
13834     },
13835     
13836     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13837         if(this.tree.hlDrop){
13838             dropNode.ui.focus();
13839             dropNode.ui.highlight();
13840         }
13841         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13842     },
13843     
13844     getTree : function(){
13845         return this.tree;
13846     },
13847     
13848     removeDropIndicators : function(n){
13849         if(n && n.ddel){
13850             var el = n.ddel;
13851             Roo.fly(el).removeClass([
13852                     "x-tree-drag-insert-above",
13853                     "x-tree-drag-insert-below",
13854                     "x-tree-drag-append"]);
13855             this.lastInsertClass = "_noclass";
13856         }
13857     },
13858     
13859     beforeDragDrop : function(target, e, id){
13860         this.cancelExpand();
13861         return true;
13862     },
13863     
13864     afterRepair : function(data){
13865         if(data && Roo.enableFx){
13866             data.node.ui.highlight();
13867         }
13868         this.hideProxy();
13869     } 
13870     
13871 });
13872
13873 }
13874 /*
13875  * Based on:
13876  * Ext JS Library 1.1.1
13877  * Copyright(c) 2006-2007, Ext JS, LLC.
13878  *
13879  * Originally Released Under LGPL - original licence link has changed is not relivant.
13880  *
13881  * Fork - LGPL
13882  * <script type="text/javascript">
13883  */
13884  
13885
13886 if(Roo.dd.DragZone){
13887 Roo.tree.TreeDragZone = function(tree, config){
13888     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13889     this.tree = tree;
13890 };
13891
13892 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13893     ddGroup : "TreeDD",
13894    
13895     onBeforeDrag : function(data, e){
13896         var n = data.node;
13897         return n && n.draggable && !n.disabled;
13898     },
13899      
13900     
13901     onInitDrag : function(e){
13902         var data = this.dragData;
13903         this.tree.getSelectionModel().select(data.node);
13904         this.proxy.update("");
13905         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13906         this.tree.fireEvent("startdrag", this.tree, data.node, e);
13907     },
13908     
13909     getRepairXY : function(e, data){
13910         return data.node.ui.getDDRepairXY();
13911     },
13912     
13913     onEndDrag : function(data, e){
13914         this.tree.fireEvent("enddrag", this.tree, data.node, e);
13915         
13916         
13917     },
13918     
13919     onValidDrop : function(dd, e, id){
13920         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13921         this.hideProxy();
13922     },
13923     
13924     beforeInvalidDrop : function(e, id){
13925         // this scrolls the original position back into view
13926         var sm = this.tree.getSelectionModel();
13927         sm.clearSelections();
13928         sm.select(this.dragData.node);
13929     }
13930 });
13931 }/*
13932  * Based on:
13933  * Ext JS Library 1.1.1
13934  * Copyright(c) 2006-2007, Ext JS, LLC.
13935  *
13936  * Originally Released Under LGPL - original licence link has changed is not relivant.
13937  *
13938  * Fork - LGPL
13939  * <script type="text/javascript">
13940  */
13941 /**
13942  * @class Roo.tree.TreeEditor
13943  * @extends Roo.Editor
13944  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
13945  * as the editor field.
13946  * @constructor
13947  * @param {Object} config (used to be the tree panel.)
13948  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
13949  * 
13950  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
13951  * @cfg {Roo.form.TextField|Object} field The field configuration
13952  *
13953  * 
13954  */
13955 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
13956     var tree = config;
13957     var field;
13958     if (oldconfig) { // old style..
13959         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
13960     } else {
13961         // new style..
13962         tree = config.tree;
13963         config.field = config.field  || {};
13964         config.field.xtype = 'TextField';
13965         field = Roo.factory(config.field, Roo.form);
13966     }
13967     config = config || {};
13968     
13969     
13970     this.addEvents({
13971         /**
13972          * @event beforenodeedit
13973          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13974          * false from the handler of this event.
13975          * @param {Editor} this
13976          * @param {Roo.tree.Node} node 
13977          */
13978         "beforenodeedit" : true
13979     });
13980     
13981     //Roo.log(config);
13982     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
13983
13984     this.tree = tree;
13985
13986     tree.on('beforeclick', this.beforeNodeClick, this);
13987     tree.getTreeEl().on('mousedown', this.hide, this);
13988     this.on('complete', this.updateNode, this);
13989     this.on('beforestartedit', this.fitToTree, this);
13990     this.on('startedit', this.bindScroll, this, {delay:10});
13991     this.on('specialkey', this.onSpecialKey, this);
13992 };
13993
13994 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
13995     /**
13996      * @cfg {String} alignment
13997      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
13998      */
13999     alignment: "l-l",
14000     // inherit
14001     autoSize: false,
14002     /**
14003      * @cfg {Boolean} hideEl
14004      * True to hide the bound element while the editor is displayed (defaults to false)
14005      */
14006     hideEl : false,
14007     /**
14008      * @cfg {String} cls
14009      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14010      */
14011     cls: "x-small-editor x-tree-editor",
14012     /**
14013      * @cfg {Boolean} shim
14014      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14015      */
14016     shim:false,
14017     // inherit
14018     shadow:"frame",
14019     /**
14020      * @cfg {Number} maxWidth
14021      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14022      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14023      * scroll and client offsets into account prior to each edit.
14024      */
14025     maxWidth: 250,
14026
14027     editDelay : 350,
14028
14029     // private
14030     fitToTree : function(ed, el){
14031         var td = this.tree.getTreeEl().dom, nd = el.dom;
14032         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14033             td.scrollLeft = nd.offsetLeft;
14034         }
14035         var w = Math.min(
14036                 this.maxWidth,
14037                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14038         this.setSize(w, '');
14039         
14040         return this.fireEvent('beforenodeedit', this, this.editNode);
14041         
14042     },
14043
14044     // private
14045     triggerEdit : function(node){
14046         this.completeEdit();
14047         this.editNode = node;
14048         this.startEdit(node.ui.textNode, node.text);
14049     },
14050
14051     // private
14052     bindScroll : function(){
14053         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14054     },
14055
14056     // private
14057     beforeNodeClick : function(node, e){
14058         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14059         this.lastClick = new Date();
14060         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14061             e.stopEvent();
14062             this.triggerEdit(node);
14063             return false;
14064         }
14065         return true;
14066     },
14067
14068     // private
14069     updateNode : function(ed, value){
14070         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14071         this.editNode.setText(value);
14072     },
14073
14074     // private
14075     onHide : function(){
14076         Roo.tree.TreeEditor.superclass.onHide.call(this);
14077         if(this.editNode){
14078             this.editNode.ui.focus();
14079         }
14080     },
14081
14082     // private
14083     onSpecialKey : function(field, e){
14084         var k = e.getKey();
14085         if(k == e.ESC){
14086             e.stopEvent();
14087             this.cancelEdit();
14088         }else if(k == e.ENTER && !e.hasModifier()){
14089             e.stopEvent();
14090             this.completeEdit();
14091         }
14092     }
14093 });//<Script type="text/javascript">
14094 /*
14095  * Based on:
14096  * Ext JS Library 1.1.1
14097  * Copyright(c) 2006-2007, Ext JS, LLC.
14098  *
14099  * Originally Released Under LGPL - original licence link has changed is not relivant.
14100  *
14101  * Fork - LGPL
14102  * <script type="text/javascript">
14103  */
14104  
14105 /**
14106  * Not documented??? - probably should be...
14107  */
14108
14109 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14110     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14111     
14112     renderElements : function(n, a, targetNode, bulkRender){
14113         //consel.log("renderElements?");
14114         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14115
14116         var t = n.getOwnerTree();
14117         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14118         
14119         var cols = t.columns;
14120         var bw = t.borderWidth;
14121         var c = cols[0];
14122         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14123          var cb = typeof a.checked == "boolean";
14124         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14125         var colcls = 'x-t-' + tid + '-c0';
14126         var buf = [
14127             '<li class="x-tree-node">',
14128             
14129                 
14130                 '<div class="x-tree-node-el ', a.cls,'">',
14131                     // extran...
14132                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14133                 
14134                 
14135                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14136                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14137                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14138                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14139                            (a.iconCls ? ' '+a.iconCls : ''),
14140                            '" unselectable="on" />',
14141                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14142                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14143                              
14144                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14145                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14146                             '<span unselectable="on" qtip="' + tx + '">',
14147                              tx,
14148                              '</span></a>' ,
14149                     '</div>',
14150                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14151                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14152                  ];
14153         for(var i = 1, len = cols.length; i < len; i++){
14154             c = cols[i];
14155             colcls = 'x-t-' + tid + '-c' +i;
14156             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14157             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14158                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14159                       "</div>");
14160          }
14161          
14162          buf.push(
14163             '</a>',
14164             '<div class="x-clear"></div></div>',
14165             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14166             "</li>");
14167         
14168         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14169             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14170                                 n.nextSibling.ui.getEl(), buf.join(""));
14171         }else{
14172             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14173         }
14174         var el = this.wrap.firstChild;
14175         this.elRow = el;
14176         this.elNode = el.firstChild;
14177         this.ranchor = el.childNodes[1];
14178         this.ctNode = this.wrap.childNodes[1];
14179         var cs = el.firstChild.childNodes;
14180         this.indentNode = cs[0];
14181         this.ecNode = cs[1];
14182         this.iconNode = cs[2];
14183         var index = 3;
14184         if(cb){
14185             this.checkbox = cs[3];
14186             index++;
14187         }
14188         this.anchor = cs[index];
14189         
14190         this.textNode = cs[index].firstChild;
14191         
14192         //el.on("click", this.onClick, this);
14193         //el.on("dblclick", this.onDblClick, this);
14194         
14195         
14196        // console.log(this);
14197     },
14198     initEvents : function(){
14199         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14200         
14201             
14202         var a = this.ranchor;
14203
14204         var el = Roo.get(a);
14205
14206         if(Roo.isOpera){ // opera render bug ignores the CSS
14207             el.setStyle("text-decoration", "none");
14208         }
14209
14210         el.on("click", this.onClick, this);
14211         el.on("dblclick", this.onDblClick, this);
14212         el.on("contextmenu", this.onContextMenu, this);
14213         
14214     },
14215     
14216     /*onSelectedChange : function(state){
14217         if(state){
14218             this.focus();
14219             this.addClass("x-tree-selected");
14220         }else{
14221             //this.blur();
14222             this.removeClass("x-tree-selected");
14223         }
14224     },*/
14225     addClass : function(cls){
14226         if(this.elRow){
14227             Roo.fly(this.elRow).addClass(cls);
14228         }
14229         
14230     },
14231     
14232     
14233     removeClass : function(cls){
14234         if(this.elRow){
14235             Roo.fly(this.elRow).removeClass(cls);
14236         }
14237     }
14238
14239     
14240     
14241 });//<Script type="text/javascript">
14242
14243 /*
14244  * Based on:
14245  * Ext JS Library 1.1.1
14246  * Copyright(c) 2006-2007, Ext JS, LLC.
14247  *
14248  * Originally Released Under LGPL - original licence link has changed is not relivant.
14249  *
14250  * Fork - LGPL
14251  * <script type="text/javascript">
14252  */
14253  
14254
14255 /**
14256  * @class Roo.tree.ColumnTree
14257  * @extends Roo.data.TreePanel
14258  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14259  * @cfg {int} borderWidth  compined right/left border allowance
14260  * @constructor
14261  * @param {String/HTMLElement/Element} el The container element
14262  * @param {Object} config
14263  */
14264 Roo.tree.ColumnTree =  function(el, config)
14265 {
14266    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14267    this.addEvents({
14268         /**
14269         * @event resize
14270         * Fire this event on a container when it resizes
14271         * @param {int} w Width
14272         * @param {int} h Height
14273         */
14274        "resize" : true
14275     });
14276     this.on('resize', this.onResize, this);
14277 };
14278
14279 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14280     //lines:false,
14281     
14282     
14283     borderWidth: Roo.isBorderBox ? 0 : 2, 
14284     headEls : false,
14285     
14286     render : function(){
14287         // add the header.....
14288        
14289         Roo.tree.ColumnTree.superclass.render.apply(this);
14290         
14291         this.el.addClass('x-column-tree');
14292         
14293         this.headers = this.el.createChild(
14294             {cls:'x-tree-headers'},this.innerCt.dom);
14295    
14296         var cols = this.columns, c;
14297         var totalWidth = 0;
14298         this.headEls = [];
14299         var  len = cols.length;
14300         for(var i = 0; i < len; i++){
14301              c = cols[i];
14302              totalWidth += c.width;
14303             this.headEls.push(this.headers.createChild({
14304                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14305                  cn: {
14306                      cls:'x-tree-hd-text',
14307                      html: c.header
14308                  },
14309                  style:'width:'+(c.width-this.borderWidth)+'px;'
14310              }));
14311         }
14312         this.headers.createChild({cls:'x-clear'});
14313         // prevent floats from wrapping when clipped
14314         this.headers.setWidth(totalWidth);
14315         //this.innerCt.setWidth(totalWidth);
14316         this.innerCt.setStyle({ overflow: 'auto' });
14317         this.onResize(this.width, this.height);
14318              
14319         
14320     },
14321     onResize : function(w,h)
14322     {
14323         this.height = h;
14324         this.width = w;
14325         // resize cols..
14326         this.innerCt.setWidth(this.width);
14327         this.innerCt.setHeight(this.height-20);
14328         
14329         // headers...
14330         var cols = this.columns, c;
14331         var totalWidth = 0;
14332         var expEl = false;
14333         var len = cols.length;
14334         for(var i = 0; i < len; i++){
14335             c = cols[i];
14336             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14337                 // it's the expander..
14338                 expEl  = this.headEls[i];
14339                 continue;
14340             }
14341             totalWidth += c.width;
14342             
14343         }
14344         if (expEl) {
14345             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14346         }
14347         this.headers.setWidth(w-20);
14348
14349         
14350         
14351         
14352     }
14353 });
14354 /*
14355  * Based on:
14356  * Ext JS Library 1.1.1
14357  * Copyright(c) 2006-2007, Ext JS, LLC.
14358  *
14359  * Originally Released Under LGPL - original licence link has changed is not relivant.
14360  *
14361  * Fork - LGPL
14362  * <script type="text/javascript">
14363  */
14364  
14365 /**
14366  * @class Roo.menu.Menu
14367  * @extends Roo.util.Observable
14368  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14369  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14370  * @constructor
14371  * Creates a new Menu
14372  * @param {Object} config Configuration options
14373  */
14374 Roo.menu.Menu = function(config){
14375     
14376     Roo.menu.Menu.superclass.constructor.call(this, config);
14377     
14378     this.id = this.id || Roo.id();
14379     this.addEvents({
14380         /**
14381          * @event beforeshow
14382          * Fires before this menu is displayed
14383          * @param {Roo.menu.Menu} this
14384          */
14385         beforeshow : true,
14386         /**
14387          * @event beforehide
14388          * Fires before this menu is hidden
14389          * @param {Roo.menu.Menu} this
14390          */
14391         beforehide : true,
14392         /**
14393          * @event show
14394          * Fires after this menu is displayed
14395          * @param {Roo.menu.Menu} this
14396          */
14397         show : true,
14398         /**
14399          * @event hide
14400          * Fires after this menu is hidden
14401          * @param {Roo.menu.Menu} this
14402          */
14403         hide : true,
14404         /**
14405          * @event click
14406          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14407          * @param {Roo.menu.Menu} this
14408          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14409          * @param {Roo.EventObject} e
14410          */
14411         click : true,
14412         /**
14413          * @event mouseover
14414          * Fires when the mouse is hovering over this menu
14415          * @param {Roo.menu.Menu} this
14416          * @param {Roo.EventObject} e
14417          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14418          */
14419         mouseover : true,
14420         /**
14421          * @event mouseout
14422          * Fires when the mouse exits this menu
14423          * @param {Roo.menu.Menu} this
14424          * @param {Roo.EventObject} e
14425          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14426          */
14427         mouseout : true,
14428         /**
14429          * @event itemclick
14430          * Fires when a menu item contained in this menu is clicked
14431          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14432          * @param {Roo.EventObject} e
14433          */
14434         itemclick: true
14435     });
14436     if (this.registerMenu) {
14437         Roo.menu.MenuMgr.register(this);
14438     }
14439     
14440     var mis = this.items;
14441     this.items = new Roo.util.MixedCollection();
14442     if(mis){
14443         this.add.apply(this, mis);
14444     }
14445 };
14446
14447 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14448     /**
14449      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14450      */
14451     minWidth : 120,
14452     /**
14453      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14454      * for bottom-right shadow (defaults to "sides")
14455      */
14456     shadow : "sides",
14457     /**
14458      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14459      * this menu (defaults to "tl-tr?")
14460      */
14461     subMenuAlign : "tl-tr?",
14462     /**
14463      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14464      * relative to its element of origin (defaults to "tl-bl?")
14465      */
14466     defaultAlign : "tl-bl?",
14467     /**
14468      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14469      */
14470     allowOtherMenus : false,
14471     /**
14472      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14473      */
14474     registerMenu : true,
14475
14476     hidden:true,
14477
14478     // private
14479     render : function(){
14480         if(this.el){
14481             return;
14482         }
14483         var el = this.el = new Roo.Layer({
14484             cls: "x-menu",
14485             shadow:this.shadow,
14486             constrain: false,
14487             parentEl: this.parentEl || document.body,
14488             zindex:15000
14489         });
14490
14491         this.keyNav = new Roo.menu.MenuNav(this);
14492
14493         if(this.plain){
14494             el.addClass("x-menu-plain");
14495         }
14496         if(this.cls){
14497             el.addClass(this.cls);
14498         }
14499         // generic focus element
14500         this.focusEl = el.createChild({
14501             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14502         });
14503         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14504         //disabling touch- as it's causing issues ..
14505         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14506         ul.on('click'   , this.onClick, this);
14507         
14508         
14509         ul.on("mouseover", this.onMouseOver, this);
14510         ul.on("mouseout", this.onMouseOut, this);
14511         this.items.each(function(item){
14512             if (item.hidden) {
14513                 return;
14514             }
14515             
14516             var li = document.createElement("li");
14517             li.className = "x-menu-list-item";
14518             ul.dom.appendChild(li);
14519             item.render(li, this);
14520         }, this);
14521         this.ul = ul;
14522         this.autoWidth();
14523     },
14524
14525     // private
14526     autoWidth : function(){
14527         var el = this.el, ul = this.ul;
14528         if(!el){
14529             return;
14530         }
14531         var w = this.width;
14532         if(w){
14533             el.setWidth(w);
14534         }else if(Roo.isIE){
14535             el.setWidth(this.minWidth);
14536             var t = el.dom.offsetWidth; // force recalc
14537             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14538         }
14539     },
14540
14541     // private
14542     delayAutoWidth : function(){
14543         if(this.rendered){
14544             if(!this.awTask){
14545                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14546             }
14547             this.awTask.delay(20);
14548         }
14549     },
14550
14551     // private
14552     findTargetItem : function(e){
14553         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14554         if(t && t.menuItemId){
14555             return this.items.get(t.menuItemId);
14556         }
14557     },
14558
14559     // private
14560     onClick : function(e){
14561         Roo.log("menu.onClick");
14562         var t = this.findTargetItem(e);
14563         if(!t){
14564             return;
14565         }
14566         Roo.log(e);
14567         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14568             if(t == this.activeItem && t.shouldDeactivate(e)){
14569                 this.activeItem.deactivate();
14570                 delete this.activeItem;
14571                 return;
14572             }
14573             if(t.canActivate){
14574                 this.setActiveItem(t, true);
14575             }
14576             return;
14577             
14578             
14579         }
14580         
14581         t.onClick(e);
14582         this.fireEvent("click", this, t, e);
14583     },
14584
14585     // private
14586     setActiveItem : function(item, autoExpand){
14587         if(item != this.activeItem){
14588             if(this.activeItem){
14589                 this.activeItem.deactivate();
14590             }
14591             this.activeItem = item;
14592             item.activate(autoExpand);
14593         }else if(autoExpand){
14594             item.expandMenu();
14595         }
14596     },
14597
14598     // private
14599     tryActivate : function(start, step){
14600         var items = this.items;
14601         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14602             var item = items.get(i);
14603             if(!item.disabled && item.canActivate){
14604                 this.setActiveItem(item, false);
14605                 return item;
14606             }
14607         }
14608         return false;
14609     },
14610
14611     // private
14612     onMouseOver : function(e){
14613         var t;
14614         if(t = this.findTargetItem(e)){
14615             if(t.canActivate && !t.disabled){
14616                 this.setActiveItem(t, true);
14617             }
14618         }
14619         this.fireEvent("mouseover", this, e, t);
14620     },
14621
14622     // private
14623     onMouseOut : function(e){
14624         var t;
14625         if(t = this.findTargetItem(e)){
14626             if(t == this.activeItem && t.shouldDeactivate(e)){
14627                 this.activeItem.deactivate();
14628                 delete this.activeItem;
14629             }
14630         }
14631         this.fireEvent("mouseout", this, e, t);
14632     },
14633
14634     /**
14635      * Read-only.  Returns true if the menu is currently displayed, else false.
14636      * @type Boolean
14637      */
14638     isVisible : function(){
14639         return this.el && !this.hidden;
14640     },
14641
14642     /**
14643      * Displays this menu relative to another element
14644      * @param {String/HTMLElement/Roo.Element} element The element to align to
14645      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14646      * the element (defaults to this.defaultAlign)
14647      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14648      */
14649     show : function(el, pos, parentMenu){
14650         this.parentMenu = parentMenu;
14651         if(!this.el){
14652             this.render();
14653         }
14654         this.fireEvent("beforeshow", this);
14655         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14656     },
14657
14658     /**
14659      * Displays this menu at a specific xy position
14660      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14661      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14662      */
14663     showAt : function(xy, parentMenu, /* private: */_e){
14664         this.parentMenu = parentMenu;
14665         if(!this.el){
14666             this.render();
14667         }
14668         if(_e !== false){
14669             this.fireEvent("beforeshow", this);
14670             xy = this.el.adjustForConstraints(xy);
14671         }
14672         this.el.setXY(xy);
14673         this.el.show();
14674         this.hidden = false;
14675         this.focus();
14676         this.fireEvent("show", this);
14677     },
14678
14679     focus : function(){
14680         if(!this.hidden){
14681             this.doFocus.defer(50, this);
14682         }
14683     },
14684
14685     doFocus : function(){
14686         if(!this.hidden){
14687             this.focusEl.focus();
14688         }
14689     },
14690
14691     /**
14692      * Hides this menu and optionally all parent menus
14693      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14694      */
14695     hide : function(deep){
14696         if(this.el && this.isVisible()){
14697             this.fireEvent("beforehide", this);
14698             if(this.activeItem){
14699                 this.activeItem.deactivate();
14700                 this.activeItem = null;
14701             }
14702             this.el.hide();
14703             this.hidden = true;
14704             this.fireEvent("hide", this);
14705         }
14706         if(deep === true && this.parentMenu){
14707             this.parentMenu.hide(true);
14708         }
14709     },
14710
14711     /**
14712      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14713      * Any of the following are valid:
14714      * <ul>
14715      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14716      * <li>An HTMLElement object which will be converted to a menu item</li>
14717      * <li>A menu item config object that will be created as a new menu item</li>
14718      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14719      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14720      * </ul>
14721      * Usage:
14722      * <pre><code>
14723 // Create the menu
14724 var menu = new Roo.menu.Menu();
14725
14726 // Create a menu item to add by reference
14727 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14728
14729 // Add a bunch of items at once using different methods.
14730 // Only the last item added will be returned.
14731 var item = menu.add(
14732     menuItem,                // add existing item by ref
14733     'Dynamic Item',          // new TextItem
14734     '-',                     // new separator
14735     { text: 'Config Item' }  // new item by config
14736 );
14737 </code></pre>
14738      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14739      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14740      */
14741     add : function(){
14742         var a = arguments, l = a.length, item;
14743         for(var i = 0; i < l; i++){
14744             var el = a[i];
14745             if ((typeof(el) == "object") && el.xtype && el.xns) {
14746                 el = Roo.factory(el, Roo.menu);
14747             }
14748             
14749             if(el.render){ // some kind of Item
14750                 item = this.addItem(el);
14751             }else if(typeof el == "string"){ // string
14752                 if(el == "separator" || el == "-"){
14753                     item = this.addSeparator();
14754                 }else{
14755                     item = this.addText(el);
14756                 }
14757             }else if(el.tagName || el.el){ // element
14758                 item = this.addElement(el);
14759             }else if(typeof el == "object"){ // must be menu item config?
14760                 item = this.addMenuItem(el);
14761             }
14762         }
14763         return item;
14764     },
14765
14766     /**
14767      * Returns this menu's underlying {@link Roo.Element} object
14768      * @return {Roo.Element} The element
14769      */
14770     getEl : function(){
14771         if(!this.el){
14772             this.render();
14773         }
14774         return this.el;
14775     },
14776
14777     /**
14778      * Adds a separator bar to the menu
14779      * @return {Roo.menu.Item} The menu item that was added
14780      */
14781     addSeparator : function(){
14782         return this.addItem(new Roo.menu.Separator());
14783     },
14784
14785     /**
14786      * Adds an {@link Roo.Element} object to the menu
14787      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14788      * @return {Roo.menu.Item} The menu item that was added
14789      */
14790     addElement : function(el){
14791         return this.addItem(new Roo.menu.BaseItem(el));
14792     },
14793
14794     /**
14795      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14796      * @param {Roo.menu.Item} item The menu item to add
14797      * @return {Roo.menu.Item} The menu item that was added
14798      */
14799     addItem : function(item){
14800         this.items.add(item);
14801         if(this.ul){
14802             var li = document.createElement("li");
14803             li.className = "x-menu-list-item";
14804             this.ul.dom.appendChild(li);
14805             item.render(li, this);
14806             this.delayAutoWidth();
14807         }
14808         return item;
14809     },
14810
14811     /**
14812      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14813      * @param {Object} config A MenuItem config object
14814      * @return {Roo.menu.Item} The menu item that was added
14815      */
14816     addMenuItem : function(config){
14817         if(!(config instanceof Roo.menu.Item)){
14818             if(typeof config.checked == "boolean"){ // must be check menu item config?
14819                 config = new Roo.menu.CheckItem(config);
14820             }else{
14821                 config = new Roo.menu.Item(config);
14822             }
14823         }
14824         return this.addItem(config);
14825     },
14826
14827     /**
14828      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14829      * @param {String} text The text to display in the menu item
14830      * @return {Roo.menu.Item} The menu item that was added
14831      */
14832     addText : function(text){
14833         return this.addItem(new Roo.menu.TextItem({ text : text }));
14834     },
14835
14836     /**
14837      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14838      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14839      * @param {Roo.menu.Item} item The menu item to add
14840      * @return {Roo.menu.Item} The menu item that was added
14841      */
14842     insert : function(index, item){
14843         this.items.insert(index, item);
14844         if(this.ul){
14845             var li = document.createElement("li");
14846             li.className = "x-menu-list-item";
14847             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14848             item.render(li, this);
14849             this.delayAutoWidth();
14850         }
14851         return item;
14852     },
14853
14854     /**
14855      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14856      * @param {Roo.menu.Item} item The menu item to remove
14857      */
14858     remove : function(item){
14859         this.items.removeKey(item.id);
14860         item.destroy();
14861     },
14862
14863     /**
14864      * Removes and destroys all items in the menu
14865      */
14866     removeAll : function(){
14867         var f;
14868         while(f = this.items.first()){
14869             this.remove(f);
14870         }
14871     }
14872 });
14873
14874 // MenuNav is a private utility class used internally by the Menu
14875 Roo.menu.MenuNav = function(menu){
14876     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14877     this.scope = this.menu = menu;
14878 };
14879
14880 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14881     doRelay : function(e, h){
14882         var k = e.getKey();
14883         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14884             this.menu.tryActivate(0, 1);
14885             return false;
14886         }
14887         return h.call(this.scope || this, e, this.menu);
14888     },
14889
14890     up : function(e, m){
14891         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14892             m.tryActivate(m.items.length-1, -1);
14893         }
14894     },
14895
14896     down : function(e, m){
14897         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14898             m.tryActivate(0, 1);
14899         }
14900     },
14901
14902     right : function(e, m){
14903         if(m.activeItem){
14904             m.activeItem.expandMenu(true);
14905         }
14906     },
14907
14908     left : function(e, m){
14909         m.hide();
14910         if(m.parentMenu && m.parentMenu.activeItem){
14911             m.parentMenu.activeItem.activate();
14912         }
14913     },
14914
14915     enter : function(e, m){
14916         if(m.activeItem){
14917             e.stopPropagation();
14918             m.activeItem.onClick(e);
14919             m.fireEvent("click", this, m.activeItem);
14920             return true;
14921         }
14922     }
14923 });/*
14924  * Based on:
14925  * Ext JS Library 1.1.1
14926  * Copyright(c) 2006-2007, Ext JS, LLC.
14927  *
14928  * Originally Released Under LGPL - original licence link has changed is not relivant.
14929  *
14930  * Fork - LGPL
14931  * <script type="text/javascript">
14932  */
14933  
14934 /**
14935  * @class Roo.menu.MenuMgr
14936  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
14937  * @singleton
14938  */
14939 Roo.menu.MenuMgr = function(){
14940    var menus, active, groups = {}, attached = false, lastShow = new Date();
14941
14942    // private - called when first menu is created
14943    function init(){
14944        menus = {};
14945        active = new Roo.util.MixedCollection();
14946        Roo.get(document).addKeyListener(27, function(){
14947            if(active.length > 0){
14948                hideAll();
14949            }
14950        });
14951    }
14952
14953    // private
14954    function hideAll(){
14955        if(active && active.length > 0){
14956            var c = active.clone();
14957            c.each(function(m){
14958                m.hide();
14959            });
14960        }
14961    }
14962
14963    // private
14964    function onHide(m){
14965        active.remove(m);
14966        if(active.length < 1){
14967            Roo.get(document).un("mousedown", onMouseDown);
14968            attached = false;
14969        }
14970    }
14971
14972    // private
14973    function onShow(m){
14974        var last = active.last();
14975        lastShow = new Date();
14976        active.add(m);
14977        if(!attached){
14978            Roo.get(document).on("mousedown", onMouseDown);
14979            attached = true;
14980        }
14981        if(m.parentMenu){
14982           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
14983           m.parentMenu.activeChild = m;
14984        }else if(last && last.isVisible()){
14985           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
14986        }
14987    }
14988
14989    // private
14990    function onBeforeHide(m){
14991        if(m.activeChild){
14992            m.activeChild.hide();
14993        }
14994        if(m.autoHideTimer){
14995            clearTimeout(m.autoHideTimer);
14996            delete m.autoHideTimer;
14997        }
14998    }
14999
15000    // private
15001    function onBeforeShow(m){
15002        var pm = m.parentMenu;
15003        if(!pm && !m.allowOtherMenus){
15004            hideAll();
15005        }else if(pm && pm.activeChild && active != m){
15006            pm.activeChild.hide();
15007        }
15008    }
15009
15010    // private
15011    function onMouseDown(e){
15012        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15013            hideAll();
15014        }
15015    }
15016
15017    // private
15018    function onBeforeCheck(mi, state){
15019        if(state){
15020            var g = groups[mi.group];
15021            for(var i = 0, l = g.length; i < l; i++){
15022                if(g[i] != mi){
15023                    g[i].setChecked(false);
15024                }
15025            }
15026        }
15027    }
15028
15029    return {
15030
15031        /**
15032         * Hides all menus that are currently visible
15033         */
15034        hideAll : function(){
15035             hideAll();  
15036        },
15037
15038        // private
15039        register : function(menu){
15040            if(!menus){
15041                init();
15042            }
15043            menus[menu.id] = menu;
15044            menu.on("beforehide", onBeforeHide);
15045            menu.on("hide", onHide);
15046            menu.on("beforeshow", onBeforeShow);
15047            menu.on("show", onShow);
15048            var g = menu.group;
15049            if(g && menu.events["checkchange"]){
15050                if(!groups[g]){
15051                    groups[g] = [];
15052                }
15053                groups[g].push(menu);
15054                menu.on("checkchange", onCheck);
15055            }
15056        },
15057
15058         /**
15059          * Returns a {@link Roo.menu.Menu} object
15060          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15061          * be used to generate and return a new Menu instance.
15062          */
15063        get : function(menu){
15064            if(typeof menu == "string"){ // menu id
15065                return menus[menu];
15066            }else if(menu.events){  // menu instance
15067                return menu;
15068            }else if(typeof menu.length == 'number'){ // array of menu items?
15069                return new Roo.menu.Menu({items:menu});
15070            }else{ // otherwise, must be a config
15071                return new Roo.menu.Menu(menu);
15072            }
15073        },
15074
15075        // private
15076        unregister : function(menu){
15077            delete menus[menu.id];
15078            menu.un("beforehide", onBeforeHide);
15079            menu.un("hide", onHide);
15080            menu.un("beforeshow", onBeforeShow);
15081            menu.un("show", onShow);
15082            var g = menu.group;
15083            if(g && menu.events["checkchange"]){
15084                groups[g].remove(menu);
15085                menu.un("checkchange", onCheck);
15086            }
15087        },
15088
15089        // private
15090        registerCheckable : function(menuItem){
15091            var g = menuItem.group;
15092            if(g){
15093                if(!groups[g]){
15094                    groups[g] = [];
15095                }
15096                groups[g].push(menuItem);
15097                menuItem.on("beforecheckchange", onBeforeCheck);
15098            }
15099        },
15100
15101        // private
15102        unregisterCheckable : function(menuItem){
15103            var g = menuItem.group;
15104            if(g){
15105                groups[g].remove(menuItem);
15106                menuItem.un("beforecheckchange", onBeforeCheck);
15107            }
15108        }
15109    };
15110 }();/*
15111  * Based on:
15112  * Ext JS Library 1.1.1
15113  * Copyright(c) 2006-2007, Ext JS, LLC.
15114  *
15115  * Originally Released Under LGPL - original licence link has changed is not relivant.
15116  *
15117  * Fork - LGPL
15118  * <script type="text/javascript">
15119  */
15120  
15121
15122 /**
15123  * @class Roo.menu.BaseItem
15124  * @extends Roo.Component
15125  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15126  * management and base configuration options shared by all menu components.
15127  * @constructor
15128  * Creates a new BaseItem
15129  * @param {Object} config Configuration options
15130  */
15131 Roo.menu.BaseItem = function(config){
15132     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15133
15134     this.addEvents({
15135         /**
15136          * @event click
15137          * Fires when this item is clicked
15138          * @param {Roo.menu.BaseItem} this
15139          * @param {Roo.EventObject} e
15140          */
15141         click: true,
15142         /**
15143          * @event activate
15144          * Fires when this item is activated
15145          * @param {Roo.menu.BaseItem} this
15146          */
15147         activate : true,
15148         /**
15149          * @event deactivate
15150          * Fires when this item is deactivated
15151          * @param {Roo.menu.BaseItem} this
15152          */
15153         deactivate : true
15154     });
15155
15156     if(this.handler){
15157         this.on("click", this.handler, this.scope, true);
15158     }
15159 };
15160
15161 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15162     /**
15163      * @cfg {Function} handler
15164      * A function that will handle the click event of this menu item (defaults to undefined)
15165      */
15166     /**
15167      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15168      */
15169     canActivate : false,
15170     
15171      /**
15172      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15173      */
15174     hidden: false,
15175     
15176     /**
15177      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15178      */
15179     activeClass : "x-menu-item-active",
15180     /**
15181      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15182      */
15183     hideOnClick : true,
15184     /**
15185      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15186      */
15187     hideDelay : 100,
15188
15189     // private
15190     ctype: "Roo.menu.BaseItem",
15191
15192     // private
15193     actionMode : "container",
15194
15195     // private
15196     render : function(container, parentMenu){
15197         this.parentMenu = parentMenu;
15198         Roo.menu.BaseItem.superclass.render.call(this, container);
15199         this.container.menuItemId = this.id;
15200     },
15201
15202     // private
15203     onRender : function(container, position){
15204         this.el = Roo.get(this.el);
15205         container.dom.appendChild(this.el.dom);
15206     },
15207
15208     // private
15209     onClick : function(e){
15210         if(!this.disabled && this.fireEvent("click", this, e) !== false
15211                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15212             this.handleClick(e);
15213         }else{
15214             e.stopEvent();
15215         }
15216     },
15217
15218     // private
15219     activate : function(){
15220         if(this.disabled){
15221             return false;
15222         }
15223         var li = this.container;
15224         li.addClass(this.activeClass);
15225         this.region = li.getRegion().adjust(2, 2, -2, -2);
15226         this.fireEvent("activate", this);
15227         return true;
15228     },
15229
15230     // private
15231     deactivate : function(){
15232         this.container.removeClass(this.activeClass);
15233         this.fireEvent("deactivate", this);
15234     },
15235
15236     // private
15237     shouldDeactivate : function(e){
15238         return !this.region || !this.region.contains(e.getPoint());
15239     },
15240
15241     // private
15242     handleClick : function(e){
15243         if(this.hideOnClick){
15244             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15245         }
15246     },
15247
15248     // private
15249     expandMenu : function(autoActivate){
15250         // do nothing
15251     },
15252
15253     // private
15254     hideMenu : function(){
15255         // do nothing
15256     }
15257 });/*
15258  * Based on:
15259  * Ext JS Library 1.1.1
15260  * Copyright(c) 2006-2007, Ext JS, LLC.
15261  *
15262  * Originally Released Under LGPL - original licence link has changed is not relivant.
15263  *
15264  * Fork - LGPL
15265  * <script type="text/javascript">
15266  */
15267  
15268 /**
15269  * @class Roo.menu.Adapter
15270  * @extends Roo.menu.BaseItem
15271  * 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.
15272  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15273  * @constructor
15274  * Creates a new Adapter
15275  * @param {Object} config Configuration options
15276  */
15277 Roo.menu.Adapter = function(component, config){
15278     Roo.menu.Adapter.superclass.constructor.call(this, config);
15279     this.component = component;
15280 };
15281 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15282     // private
15283     canActivate : true,
15284
15285     // private
15286     onRender : function(container, position){
15287         this.component.render(container);
15288         this.el = this.component.getEl();
15289     },
15290
15291     // private
15292     activate : function(){
15293         if(this.disabled){
15294             return false;
15295         }
15296         this.component.focus();
15297         this.fireEvent("activate", this);
15298         return true;
15299     },
15300
15301     // private
15302     deactivate : function(){
15303         this.fireEvent("deactivate", this);
15304     },
15305
15306     // private
15307     disable : function(){
15308         this.component.disable();
15309         Roo.menu.Adapter.superclass.disable.call(this);
15310     },
15311
15312     // private
15313     enable : function(){
15314         this.component.enable();
15315         Roo.menu.Adapter.superclass.enable.call(this);
15316     }
15317 });/*
15318  * Based on:
15319  * Ext JS Library 1.1.1
15320  * Copyright(c) 2006-2007, Ext JS, LLC.
15321  *
15322  * Originally Released Under LGPL - original licence link has changed is not relivant.
15323  *
15324  * Fork - LGPL
15325  * <script type="text/javascript">
15326  */
15327
15328 /**
15329  * @class Roo.menu.TextItem
15330  * @extends Roo.menu.BaseItem
15331  * Adds a static text string to a menu, usually used as either a heading or group separator.
15332  * Note: old style constructor with text is still supported.
15333  * 
15334  * @constructor
15335  * Creates a new TextItem
15336  * @param {Object} cfg Configuration
15337  */
15338 Roo.menu.TextItem = function(cfg){
15339     if (typeof(cfg) == 'string') {
15340         this.text = cfg;
15341     } else {
15342         Roo.apply(this,cfg);
15343     }
15344     
15345     Roo.menu.TextItem.superclass.constructor.call(this);
15346 };
15347
15348 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15349     /**
15350      * @cfg {String} text Text to show on item.
15351      */
15352     text : '',
15353     
15354     /**
15355      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15356      */
15357     hideOnClick : false,
15358     /**
15359      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15360      */
15361     itemCls : "x-menu-text",
15362
15363     // private
15364     onRender : function(){
15365         var s = document.createElement("span");
15366         s.className = this.itemCls;
15367         s.innerHTML = this.text;
15368         this.el = s;
15369         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15370     }
15371 });/*
15372  * Based on:
15373  * Ext JS Library 1.1.1
15374  * Copyright(c) 2006-2007, Ext JS, LLC.
15375  *
15376  * Originally Released Under LGPL - original licence link has changed is not relivant.
15377  *
15378  * Fork - LGPL
15379  * <script type="text/javascript">
15380  */
15381
15382 /**
15383  * @class Roo.menu.Separator
15384  * @extends Roo.menu.BaseItem
15385  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15386  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15387  * @constructor
15388  * @param {Object} config Configuration options
15389  */
15390 Roo.menu.Separator = function(config){
15391     Roo.menu.Separator.superclass.constructor.call(this, config);
15392 };
15393
15394 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15395     /**
15396      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15397      */
15398     itemCls : "x-menu-sep",
15399     /**
15400      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15401      */
15402     hideOnClick : false,
15403
15404     // private
15405     onRender : function(li){
15406         var s = document.createElement("span");
15407         s.className = this.itemCls;
15408         s.innerHTML = "&#160;";
15409         this.el = s;
15410         li.addClass("x-menu-sep-li");
15411         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15412     }
15413 });/*
15414  * Based on:
15415  * Ext JS Library 1.1.1
15416  * Copyright(c) 2006-2007, Ext JS, LLC.
15417  *
15418  * Originally Released Under LGPL - original licence link has changed is not relivant.
15419  *
15420  * Fork - LGPL
15421  * <script type="text/javascript">
15422  */
15423 /**
15424  * @class Roo.menu.Item
15425  * @extends Roo.menu.BaseItem
15426  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15427  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15428  * activation and click handling.
15429  * @constructor
15430  * Creates a new Item
15431  * @param {Object} config Configuration options
15432  */
15433 Roo.menu.Item = function(config){
15434     Roo.menu.Item.superclass.constructor.call(this, config);
15435     if(this.menu){
15436         this.menu = Roo.menu.MenuMgr.get(this.menu);
15437     }
15438 };
15439 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15440     
15441     /**
15442      * @cfg {String} text
15443      * The text to show on the menu item.
15444      */
15445     text: '',
15446      /**
15447      * @cfg {String} HTML to render in menu
15448      * The text to show on the menu item (HTML version).
15449      */
15450     html: '',
15451     /**
15452      * @cfg {String} icon
15453      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15454      */
15455     icon: undefined,
15456     /**
15457      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15458      */
15459     itemCls : "x-menu-item",
15460     /**
15461      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15462      */
15463     canActivate : true,
15464     /**
15465      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15466      */
15467     showDelay: 200,
15468     // doc'd in BaseItem
15469     hideDelay: 200,
15470
15471     // private
15472     ctype: "Roo.menu.Item",
15473     
15474     // private
15475     onRender : function(container, position){
15476         var el = document.createElement("a");
15477         el.hideFocus = true;
15478         el.unselectable = "on";
15479         el.href = this.href || "#";
15480         if(this.hrefTarget){
15481             el.target = this.hrefTarget;
15482         }
15483         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15484         
15485         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15486         
15487         el.innerHTML = String.format(
15488                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15489                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15490         this.el = el;
15491         Roo.menu.Item.superclass.onRender.call(this, container, position);
15492     },
15493
15494     /**
15495      * Sets the text to display in this menu item
15496      * @param {String} text The text to display
15497      * @param {Boolean} isHTML true to indicate text is pure html.
15498      */
15499     setText : function(text, isHTML){
15500         if (isHTML) {
15501             this.html = text;
15502         } else {
15503             this.text = text;
15504             this.html = '';
15505         }
15506         if(this.rendered){
15507             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15508      
15509             this.el.update(String.format(
15510                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15511                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15512             this.parentMenu.autoWidth();
15513         }
15514     },
15515
15516     // private
15517     handleClick : function(e){
15518         if(!this.href){ // if no link defined, stop the event automatically
15519             e.stopEvent();
15520         }
15521         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15522     },
15523
15524     // private
15525     activate : function(autoExpand){
15526         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15527             this.focus();
15528             if(autoExpand){
15529                 this.expandMenu();
15530             }
15531         }
15532         return true;
15533     },
15534
15535     // private
15536     shouldDeactivate : function(e){
15537         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15538             if(this.menu && this.menu.isVisible()){
15539                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15540             }
15541             return true;
15542         }
15543         return false;
15544     },
15545
15546     // private
15547     deactivate : function(){
15548         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15549         this.hideMenu();
15550     },
15551
15552     // private
15553     expandMenu : function(autoActivate){
15554         if(!this.disabled && this.menu){
15555             clearTimeout(this.hideTimer);
15556             delete this.hideTimer;
15557             if(!this.menu.isVisible() && !this.showTimer){
15558                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15559             }else if (this.menu.isVisible() && autoActivate){
15560                 this.menu.tryActivate(0, 1);
15561             }
15562         }
15563     },
15564
15565     // private
15566     deferExpand : function(autoActivate){
15567         delete this.showTimer;
15568         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15569         if(autoActivate){
15570             this.menu.tryActivate(0, 1);
15571         }
15572     },
15573
15574     // private
15575     hideMenu : function(){
15576         clearTimeout(this.showTimer);
15577         delete this.showTimer;
15578         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15579             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15580         }
15581     },
15582
15583     // private
15584     deferHide : function(){
15585         delete this.hideTimer;
15586         this.menu.hide();
15587     }
15588 });/*
15589  * Based on:
15590  * Ext JS Library 1.1.1
15591  * Copyright(c) 2006-2007, Ext JS, LLC.
15592  *
15593  * Originally Released Under LGPL - original licence link has changed is not relivant.
15594  *
15595  * Fork - LGPL
15596  * <script type="text/javascript">
15597  */
15598  
15599 /**
15600  * @class Roo.menu.CheckItem
15601  * @extends Roo.menu.Item
15602  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15603  * @constructor
15604  * Creates a new CheckItem
15605  * @param {Object} config Configuration options
15606  */
15607 Roo.menu.CheckItem = function(config){
15608     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15609     this.addEvents({
15610         /**
15611          * @event beforecheckchange
15612          * Fires before the checked value is set, providing an opportunity to cancel if needed
15613          * @param {Roo.menu.CheckItem} this
15614          * @param {Boolean} checked The new checked value that will be set
15615          */
15616         "beforecheckchange" : true,
15617         /**
15618          * @event checkchange
15619          * Fires after the checked value has been set
15620          * @param {Roo.menu.CheckItem} this
15621          * @param {Boolean} checked The checked value that was set
15622          */
15623         "checkchange" : true
15624     });
15625     if(this.checkHandler){
15626         this.on('checkchange', this.checkHandler, this.scope);
15627     }
15628 };
15629 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15630     /**
15631      * @cfg {String} group
15632      * All check items with the same group name will automatically be grouped into a single-select
15633      * radio button group (defaults to '')
15634      */
15635     /**
15636      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15637      */
15638     itemCls : "x-menu-item x-menu-check-item",
15639     /**
15640      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15641      */
15642     groupClass : "x-menu-group-item",
15643
15644     /**
15645      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15646      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15647      * initialized with checked = true will be rendered as checked.
15648      */
15649     checked: false,
15650
15651     // private
15652     ctype: "Roo.menu.CheckItem",
15653
15654     // private
15655     onRender : function(c){
15656         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15657         if(this.group){
15658             this.el.addClass(this.groupClass);
15659         }
15660         Roo.menu.MenuMgr.registerCheckable(this);
15661         if(this.checked){
15662             this.checked = false;
15663             this.setChecked(true, true);
15664         }
15665     },
15666
15667     // private
15668     destroy : function(){
15669         if(this.rendered){
15670             Roo.menu.MenuMgr.unregisterCheckable(this);
15671         }
15672         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15673     },
15674
15675     /**
15676      * Set the checked state of this item
15677      * @param {Boolean} checked The new checked value
15678      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15679      */
15680     setChecked : function(state, suppressEvent){
15681         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15682             if(this.container){
15683                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15684             }
15685             this.checked = state;
15686             if(suppressEvent !== true){
15687                 this.fireEvent("checkchange", this, state);
15688             }
15689         }
15690     },
15691
15692     // private
15693     handleClick : function(e){
15694        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15695            this.setChecked(!this.checked);
15696        }
15697        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15698     }
15699 });/*
15700  * Based on:
15701  * Ext JS Library 1.1.1
15702  * Copyright(c) 2006-2007, Ext JS, LLC.
15703  *
15704  * Originally Released Under LGPL - original licence link has changed is not relivant.
15705  *
15706  * Fork - LGPL
15707  * <script type="text/javascript">
15708  */
15709  
15710 /**
15711  * @class Roo.menu.DateItem
15712  * @extends Roo.menu.Adapter
15713  * A menu item that wraps the {@link Roo.DatPicker} component.
15714  * @constructor
15715  * Creates a new DateItem
15716  * @param {Object} config Configuration options
15717  */
15718 Roo.menu.DateItem = function(config){
15719     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15720     /** The Roo.DatePicker object @type Roo.DatePicker */
15721     this.picker = this.component;
15722     this.addEvents({select: true});
15723     
15724     this.picker.on("render", function(picker){
15725         picker.getEl().swallowEvent("click");
15726         picker.container.addClass("x-menu-date-item");
15727     });
15728
15729     this.picker.on("select", this.onSelect, this);
15730 };
15731
15732 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15733     // private
15734     onSelect : function(picker, date){
15735         this.fireEvent("select", this, date, picker);
15736         Roo.menu.DateItem.superclass.handleClick.call(this);
15737     }
15738 });/*
15739  * Based on:
15740  * Ext JS Library 1.1.1
15741  * Copyright(c) 2006-2007, Ext JS, LLC.
15742  *
15743  * Originally Released Under LGPL - original licence link has changed is not relivant.
15744  *
15745  * Fork - LGPL
15746  * <script type="text/javascript">
15747  */
15748  
15749 /**
15750  * @class Roo.menu.ColorItem
15751  * @extends Roo.menu.Adapter
15752  * A menu item that wraps the {@link Roo.ColorPalette} component.
15753  * @constructor
15754  * Creates a new ColorItem
15755  * @param {Object} config Configuration options
15756  */
15757 Roo.menu.ColorItem = function(config){
15758     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15759     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15760     this.palette = this.component;
15761     this.relayEvents(this.palette, ["select"]);
15762     if(this.selectHandler){
15763         this.on('select', this.selectHandler, this.scope);
15764     }
15765 };
15766 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15767  * Based on:
15768  * Ext JS Library 1.1.1
15769  * Copyright(c) 2006-2007, Ext JS, LLC.
15770  *
15771  * Originally Released Under LGPL - original licence link has changed is not relivant.
15772  *
15773  * Fork - LGPL
15774  * <script type="text/javascript">
15775  */
15776  
15777
15778 /**
15779  * @class Roo.menu.DateMenu
15780  * @extends Roo.menu.Menu
15781  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15782  * @constructor
15783  * Creates a new DateMenu
15784  * @param {Object} config Configuration options
15785  */
15786 Roo.menu.DateMenu = function(config){
15787     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15788     this.plain = true;
15789     var di = new Roo.menu.DateItem(config);
15790     this.add(di);
15791     /**
15792      * The {@link Roo.DatePicker} instance for this DateMenu
15793      * @type DatePicker
15794      */
15795     this.picker = di.picker;
15796     /**
15797      * @event select
15798      * @param {DatePicker} picker
15799      * @param {Date} date
15800      */
15801     this.relayEvents(di, ["select"]);
15802     this.on('beforeshow', function(){
15803         if(this.picker){
15804             this.picker.hideMonthPicker(false);
15805         }
15806     }, this);
15807 };
15808 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15809     cls:'x-date-menu'
15810 });/*
15811  * Based on:
15812  * Ext JS Library 1.1.1
15813  * Copyright(c) 2006-2007, Ext JS, LLC.
15814  *
15815  * Originally Released Under LGPL - original licence link has changed is not relivant.
15816  *
15817  * Fork - LGPL
15818  * <script type="text/javascript">
15819  */
15820  
15821
15822 /**
15823  * @class Roo.menu.ColorMenu
15824  * @extends Roo.menu.Menu
15825  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15826  * @constructor
15827  * Creates a new ColorMenu
15828  * @param {Object} config Configuration options
15829  */
15830 Roo.menu.ColorMenu = function(config){
15831     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15832     this.plain = true;
15833     var ci = new Roo.menu.ColorItem(config);
15834     this.add(ci);
15835     /**
15836      * The {@link Roo.ColorPalette} instance for this ColorMenu
15837      * @type ColorPalette
15838      */
15839     this.palette = ci.palette;
15840     /**
15841      * @event select
15842      * @param {ColorPalette} palette
15843      * @param {String} color
15844      */
15845     this.relayEvents(ci, ["select"]);
15846 };
15847 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15848  * Based on:
15849  * Ext JS Library 1.1.1
15850  * Copyright(c) 2006-2007, Ext JS, LLC.
15851  *
15852  * Originally Released Under LGPL - original licence link has changed is not relivant.
15853  *
15854  * Fork - LGPL
15855  * <script type="text/javascript">
15856  */
15857  
15858 /**
15859  * @class Roo.form.TextItem
15860  * @extends Roo.BoxComponent
15861  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15862  * @constructor
15863  * Creates a new TextItem
15864  * @param {Object} config Configuration options
15865  */
15866 Roo.form.TextItem = function(config){
15867     Roo.form.TextItem.superclass.constructor.call(this, config);
15868 };
15869
15870 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15871     
15872     /**
15873      * @cfg {String} tag the tag for this item (default div)
15874      */
15875     tag : 'div',
15876     /**
15877      * @cfg {String} html the content for this item
15878      */
15879     html : '',
15880     
15881     getAutoCreate : function()
15882     {
15883         var cfg = {
15884             id: this.id,
15885             tag: this.tag,
15886             html: this.html,
15887             cls: 'x-form-item'
15888         };
15889         
15890         return cfg;
15891         
15892     },
15893     
15894     onRender : function(ct, position)
15895     {
15896         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15897         
15898         if(!this.el){
15899             var cfg = this.getAutoCreate();
15900             if(!cfg.name){
15901                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15902             }
15903             if (!cfg.name.length) {
15904                 delete cfg.name;
15905             }
15906             this.el = ct.createChild(cfg, position);
15907         }
15908     },
15909     /*
15910      * setHTML
15911      * @param {String} html update the Contents of the element.
15912      */
15913     setHTML : function(html)
15914     {
15915         this.fieldEl.dom.innerHTML = html;
15916     }
15917     
15918 });/*
15919  * Based on:
15920  * Ext JS Library 1.1.1
15921  * Copyright(c) 2006-2007, Ext JS, LLC.
15922  *
15923  * Originally Released Under LGPL - original licence link has changed is not relivant.
15924  *
15925  * Fork - LGPL
15926  * <script type="text/javascript">
15927  */
15928  
15929 /**
15930  * @class Roo.form.Field
15931  * @extends Roo.BoxComponent
15932  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15933  * @constructor
15934  * Creates a new Field
15935  * @param {Object} config Configuration options
15936  */
15937 Roo.form.Field = function(config){
15938     Roo.form.Field.superclass.constructor.call(this, config);
15939 };
15940
15941 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
15942     /**
15943      * @cfg {String} fieldLabel Label to use when rendering a form.
15944      */
15945        /**
15946      * @cfg {String} qtip Mouse over tip
15947      */
15948      
15949     /**
15950      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
15951      */
15952     invalidClass : "x-form-invalid",
15953     /**
15954      * @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")
15955      */
15956     invalidText : "The value in this field is invalid",
15957     /**
15958      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
15959      */
15960     focusClass : "x-form-focus",
15961     /**
15962      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
15963       automatic validation (defaults to "keyup").
15964      */
15965     validationEvent : "keyup",
15966     /**
15967      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
15968      */
15969     validateOnBlur : true,
15970     /**
15971      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
15972      */
15973     validationDelay : 250,
15974     /**
15975      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
15976      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
15977      */
15978     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
15979     /**
15980      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
15981      */
15982     fieldClass : "x-form-field",
15983     /**
15984      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
15985      *<pre>
15986 Value         Description
15987 -----------   ----------------------------------------------------------------------
15988 qtip          Display a quick tip when the user hovers over the field
15989 title         Display a default browser title attribute popup
15990 under         Add a block div beneath the field containing the error text
15991 side          Add an error icon to the right of the field with a popup on hover
15992 [element id]  Add the error text directly to the innerHTML of the specified element
15993 </pre>
15994      */
15995     msgTarget : 'qtip',
15996     /**
15997      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
15998      */
15999     msgFx : 'normal',
16000
16001     /**
16002      * @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.
16003      */
16004     readOnly : false,
16005
16006     /**
16007      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16008      */
16009     disabled : false,
16010
16011     /**
16012      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16013      */
16014     inputType : undefined,
16015     
16016     /**
16017      * @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).
16018          */
16019         tabIndex : undefined,
16020         
16021     // private
16022     isFormField : true,
16023
16024     // private
16025     hasFocus : false,
16026     /**
16027      * @property {Roo.Element} fieldEl
16028      * Element Containing the rendered Field (with label etc.)
16029      */
16030     /**
16031      * @cfg {Mixed} value A value to initialize this field with.
16032      */
16033     value : undefined,
16034
16035     /**
16036      * @cfg {String} name The field's HTML name attribute.
16037      */
16038     /**
16039      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16040      */
16041     // private
16042     loadedValue : false,
16043      
16044      
16045         // private ??
16046         initComponent : function(){
16047         Roo.form.Field.superclass.initComponent.call(this);
16048         this.addEvents({
16049             /**
16050              * @event focus
16051              * Fires when this field receives input focus.
16052              * @param {Roo.form.Field} this
16053              */
16054             focus : true,
16055             /**
16056              * @event blur
16057              * Fires when this field loses input focus.
16058              * @param {Roo.form.Field} this
16059              */
16060             blur : true,
16061             /**
16062              * @event specialkey
16063              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16064              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16065              * @param {Roo.form.Field} this
16066              * @param {Roo.EventObject} e The event object
16067              */
16068             specialkey : true,
16069             /**
16070              * @event change
16071              * Fires just before the field blurs if the field value has changed.
16072              * @param {Roo.form.Field} this
16073              * @param {Mixed} newValue The new value
16074              * @param {Mixed} oldValue The original value
16075              */
16076             change : true,
16077             /**
16078              * @event invalid
16079              * Fires after the field has been marked as invalid.
16080              * @param {Roo.form.Field} this
16081              * @param {String} msg The validation message
16082              */
16083             invalid : true,
16084             /**
16085              * @event valid
16086              * Fires after the field has been validated with no errors.
16087              * @param {Roo.form.Field} this
16088              */
16089             valid : true,
16090              /**
16091              * @event keyup
16092              * Fires after the key up
16093              * @param {Roo.form.Field} this
16094              * @param {Roo.EventObject}  e The event Object
16095              */
16096             keyup : true
16097         });
16098     },
16099
16100     /**
16101      * Returns the name attribute of the field if available
16102      * @return {String} name The field name
16103      */
16104     getName: function(){
16105          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16106     },
16107
16108     // private
16109     onRender : function(ct, position){
16110         Roo.form.Field.superclass.onRender.call(this, ct, position);
16111         if(!this.el){
16112             var cfg = this.getAutoCreate();
16113             if(!cfg.name){
16114                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16115             }
16116             if (!cfg.name.length) {
16117                 delete cfg.name;
16118             }
16119             if(this.inputType){
16120                 cfg.type = this.inputType;
16121             }
16122             this.el = ct.createChild(cfg, position);
16123         }
16124         var type = this.el.dom.type;
16125         if(type){
16126             if(type == 'password'){
16127                 type = 'text';
16128             }
16129             this.el.addClass('x-form-'+type);
16130         }
16131         if(this.readOnly){
16132             this.el.dom.readOnly = true;
16133         }
16134         if(this.tabIndex !== undefined){
16135             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16136         }
16137
16138         this.el.addClass([this.fieldClass, this.cls]);
16139         this.initValue();
16140     },
16141
16142     /**
16143      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16144      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16145      * @return {Roo.form.Field} this
16146      */
16147     applyTo : function(target){
16148         this.allowDomMove = false;
16149         this.el = Roo.get(target);
16150         this.render(this.el.dom.parentNode);
16151         return this;
16152     },
16153
16154     // private
16155     initValue : function(){
16156         if(this.value !== undefined){
16157             this.setValue(this.value);
16158         }else if(this.el.dom.value.length > 0){
16159             this.setValue(this.el.dom.value);
16160         }
16161     },
16162
16163     /**
16164      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16165      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16166      */
16167     isDirty : function() {
16168         if(this.disabled) {
16169             return false;
16170         }
16171         return String(this.getValue()) !== String(this.originalValue);
16172     },
16173
16174     /**
16175      * stores the current value in loadedValue
16176      */
16177     resetHasChanged : function()
16178     {
16179         this.loadedValue = String(this.getValue());
16180     },
16181     /**
16182      * checks the current value against the 'loaded' value.
16183      * Note - will return false if 'resetHasChanged' has not been called first.
16184      */
16185     hasChanged : function()
16186     {
16187         if(this.disabled || this.readOnly) {
16188             return false;
16189         }
16190         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16191     },
16192     
16193     
16194     
16195     // private
16196     afterRender : function(){
16197         Roo.form.Field.superclass.afterRender.call(this);
16198         this.initEvents();
16199     },
16200
16201     // private
16202     fireKey : function(e){
16203         //Roo.log('field ' + e.getKey());
16204         if(e.isNavKeyPress()){
16205             this.fireEvent("specialkey", this, e);
16206         }
16207     },
16208
16209     /**
16210      * Resets the current field value to the originally loaded value and clears any validation messages
16211      */
16212     reset : function(){
16213         this.setValue(this.resetValue);
16214         this.originalValue = this.getValue();
16215         this.clearInvalid();
16216     },
16217
16218     // private
16219     initEvents : function(){
16220         // safari killled keypress - so keydown is now used..
16221         this.el.on("keydown" , this.fireKey,  this);
16222         this.el.on("focus", this.onFocus,  this);
16223         this.el.on("blur", this.onBlur,  this);
16224         this.el.relayEvent('keyup', this);
16225
16226         // reference to original value for reset
16227         this.originalValue = this.getValue();
16228         this.resetValue =  this.getValue();
16229     },
16230
16231     // private
16232     onFocus : function(){
16233         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16234             this.el.addClass(this.focusClass);
16235         }
16236         if(!this.hasFocus){
16237             this.hasFocus = true;
16238             this.startValue = this.getValue();
16239             this.fireEvent("focus", this);
16240         }
16241     },
16242
16243     beforeBlur : Roo.emptyFn,
16244
16245     // private
16246     onBlur : function(){
16247         this.beforeBlur();
16248         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16249             this.el.removeClass(this.focusClass);
16250         }
16251         this.hasFocus = false;
16252         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16253             this.validate();
16254         }
16255         var v = this.getValue();
16256         if(String(v) !== String(this.startValue)){
16257             this.fireEvent('change', this, v, this.startValue);
16258         }
16259         this.fireEvent("blur", this);
16260     },
16261
16262     /**
16263      * Returns whether or not the field value is currently valid
16264      * @param {Boolean} preventMark True to disable marking the field invalid
16265      * @return {Boolean} True if the value is valid, else false
16266      */
16267     isValid : function(preventMark){
16268         if(this.disabled){
16269             return true;
16270         }
16271         var restore = this.preventMark;
16272         this.preventMark = preventMark === true;
16273         var v = this.validateValue(this.processValue(this.getRawValue()));
16274         this.preventMark = restore;
16275         return v;
16276     },
16277
16278     /**
16279      * Validates the field value
16280      * @return {Boolean} True if the value is valid, else false
16281      */
16282     validate : function(){
16283         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16284             this.clearInvalid();
16285             return true;
16286         }
16287         return false;
16288     },
16289
16290     processValue : function(value){
16291         return value;
16292     },
16293
16294     // private
16295     // Subclasses should provide the validation implementation by overriding this
16296     validateValue : function(value){
16297         return true;
16298     },
16299
16300     /**
16301      * Mark this field as invalid
16302      * @param {String} msg The validation message
16303      */
16304     markInvalid : function(msg){
16305         if(!this.rendered || this.preventMark){ // not rendered
16306             return;
16307         }
16308         
16309         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16310         
16311         obj.el.addClass(this.invalidClass);
16312         msg = msg || this.invalidText;
16313         switch(this.msgTarget){
16314             case 'qtip':
16315                 obj.el.dom.qtip = msg;
16316                 obj.el.dom.qclass = 'x-form-invalid-tip';
16317                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16318                     Roo.QuickTips.enable();
16319                 }
16320                 break;
16321             case 'title':
16322                 this.el.dom.title = msg;
16323                 break;
16324             case 'under':
16325                 if(!this.errorEl){
16326                     var elp = this.el.findParent('.x-form-element', 5, true);
16327                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16328                     this.errorEl.setWidth(elp.getWidth(true)-20);
16329                 }
16330                 this.errorEl.update(msg);
16331                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16332                 break;
16333             case 'side':
16334                 if(!this.errorIcon){
16335                     var elp = this.el.findParent('.x-form-element', 5, true);
16336                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16337                 }
16338                 this.alignErrorIcon();
16339                 this.errorIcon.dom.qtip = msg;
16340                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16341                 this.errorIcon.show();
16342                 this.on('resize', this.alignErrorIcon, this);
16343                 break;
16344             default:
16345                 var t = Roo.getDom(this.msgTarget);
16346                 t.innerHTML = msg;
16347                 t.style.display = this.msgDisplay;
16348                 break;
16349         }
16350         this.fireEvent('invalid', this, msg);
16351     },
16352
16353     // private
16354     alignErrorIcon : function(){
16355         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16356     },
16357
16358     /**
16359      * Clear any invalid styles/messages for this field
16360      */
16361     clearInvalid : function(){
16362         if(!this.rendered || this.preventMark){ // not rendered
16363             return;
16364         }
16365         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16366         
16367         obj.el.removeClass(this.invalidClass);
16368         switch(this.msgTarget){
16369             case 'qtip':
16370                 obj.el.dom.qtip = '';
16371                 break;
16372             case 'title':
16373                 this.el.dom.title = '';
16374                 break;
16375             case 'under':
16376                 if(this.errorEl){
16377                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16378                 }
16379                 break;
16380             case 'side':
16381                 if(this.errorIcon){
16382                     this.errorIcon.dom.qtip = '';
16383                     this.errorIcon.hide();
16384                     this.un('resize', this.alignErrorIcon, this);
16385                 }
16386                 break;
16387             default:
16388                 var t = Roo.getDom(this.msgTarget);
16389                 t.innerHTML = '';
16390                 t.style.display = 'none';
16391                 break;
16392         }
16393         this.fireEvent('valid', this);
16394     },
16395
16396     /**
16397      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16398      * @return {Mixed} value The field value
16399      */
16400     getRawValue : function(){
16401         var v = this.el.getValue();
16402         
16403         return v;
16404     },
16405
16406     /**
16407      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16408      * @return {Mixed} value The field value
16409      */
16410     getValue : function(){
16411         var v = this.el.getValue();
16412          
16413         return v;
16414     },
16415
16416     /**
16417      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16418      * @param {Mixed} value The value to set
16419      */
16420     setRawValue : function(v){
16421         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16422     },
16423
16424     /**
16425      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16426      * @param {Mixed} value The value to set
16427      */
16428     setValue : function(v){
16429         this.value = v;
16430         if(this.rendered){
16431             this.el.dom.value = (v === null || v === undefined ? '' : v);
16432              this.validate();
16433         }
16434     },
16435
16436     adjustSize : function(w, h){
16437         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16438         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16439         return s;
16440     },
16441
16442     adjustWidth : function(tag, w){
16443         tag = tag.toLowerCase();
16444         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16445             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16446                 if(tag == 'input'){
16447                     return w + 2;
16448                 }
16449                 if(tag == 'textarea'){
16450                     return w-2;
16451                 }
16452             }else if(Roo.isOpera){
16453                 if(tag == 'input'){
16454                     return w + 2;
16455                 }
16456                 if(tag == 'textarea'){
16457                     return w-2;
16458                 }
16459             }
16460         }
16461         return w;
16462     }
16463 });
16464
16465
16466 // anything other than normal should be considered experimental
16467 Roo.form.Field.msgFx = {
16468     normal : {
16469         show: function(msgEl, f){
16470             msgEl.setDisplayed('block');
16471         },
16472
16473         hide : function(msgEl, f){
16474             msgEl.setDisplayed(false).update('');
16475         }
16476     },
16477
16478     slide : {
16479         show: function(msgEl, f){
16480             msgEl.slideIn('t', {stopFx:true});
16481         },
16482
16483         hide : function(msgEl, f){
16484             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16485         }
16486     },
16487
16488     slideRight : {
16489         show: function(msgEl, f){
16490             msgEl.fixDisplay();
16491             msgEl.alignTo(f.el, 'tl-tr');
16492             msgEl.slideIn('l', {stopFx:true});
16493         },
16494
16495         hide : function(msgEl, f){
16496             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16497         }
16498     }
16499 };/*
16500  * Based on:
16501  * Ext JS Library 1.1.1
16502  * Copyright(c) 2006-2007, Ext JS, LLC.
16503  *
16504  * Originally Released Under LGPL - original licence link has changed is not relivant.
16505  *
16506  * Fork - LGPL
16507  * <script type="text/javascript">
16508  */
16509  
16510
16511 /**
16512  * @class Roo.form.TextField
16513  * @extends Roo.form.Field
16514  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16515  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16516  * @constructor
16517  * Creates a new TextField
16518  * @param {Object} config Configuration options
16519  */
16520 Roo.form.TextField = function(config){
16521     Roo.form.TextField.superclass.constructor.call(this, config);
16522     this.addEvents({
16523         /**
16524          * @event autosize
16525          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16526          * according to the default logic, but this event provides a hook for the developer to apply additional
16527          * logic at runtime to resize the field if needed.
16528              * @param {Roo.form.Field} this This text field
16529              * @param {Number} width The new field width
16530              */
16531         autosize : true
16532     });
16533 };
16534
16535 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16536     /**
16537      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16538      */
16539     grow : false,
16540     /**
16541      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16542      */
16543     growMin : 30,
16544     /**
16545      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16546      */
16547     growMax : 800,
16548     /**
16549      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16550      */
16551     vtype : null,
16552     /**
16553      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16554      */
16555     maskRe : null,
16556     /**
16557      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16558      */
16559     disableKeyFilter : false,
16560     /**
16561      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16562      */
16563     allowBlank : true,
16564     /**
16565      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16566      */
16567     minLength : 0,
16568     /**
16569      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16570      */
16571     maxLength : Number.MAX_VALUE,
16572     /**
16573      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16574      */
16575     minLengthText : "The minimum length for this field is {0}",
16576     /**
16577      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16578      */
16579     maxLengthText : "The maximum length for this field is {0}",
16580     /**
16581      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16582      */
16583     selectOnFocus : false,
16584     /**
16585      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16586      */    
16587     allowLeadingSpace : false,
16588     /**
16589      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16590      */
16591     blankText : "This field is required",
16592     /**
16593      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16594      * If available, this function will be called only after the basic validators all return true, and will be passed the
16595      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16596      */
16597     validator : null,
16598     /**
16599      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16600      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16601      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16602      */
16603     regex : null,
16604     /**
16605      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16606      */
16607     regexText : "",
16608     /**
16609      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16610      */
16611     emptyText : null,
16612    
16613
16614     // private
16615     initEvents : function()
16616     {
16617         if (this.emptyText) {
16618             this.el.attr('placeholder', this.emptyText);
16619         }
16620         
16621         Roo.form.TextField.superclass.initEvents.call(this);
16622         if(this.validationEvent == 'keyup'){
16623             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16624             this.el.on('keyup', this.filterValidation, this);
16625         }
16626         else if(this.validationEvent !== false){
16627             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16628         }
16629         
16630         if(this.selectOnFocus){
16631             this.on("focus", this.preFocus, this);
16632         }
16633         if (!this.allowLeadingSpace) {
16634             this.on('blur', this.cleanLeadingSpace, this);
16635         }
16636         
16637         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16638             this.el.on("keypress", this.filterKeys, this);
16639         }
16640         if(this.grow){
16641             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16642             this.el.on("click", this.autoSize,  this);
16643         }
16644         if(this.el.is('input[type=password]') && Roo.isSafari){
16645             this.el.on('keydown', this.SafariOnKeyDown, this);
16646         }
16647     },
16648
16649     processValue : function(value){
16650         if(this.stripCharsRe){
16651             var newValue = value.replace(this.stripCharsRe, '');
16652             if(newValue !== value){
16653                 this.setRawValue(newValue);
16654                 return newValue;
16655             }
16656         }
16657         return value;
16658     },
16659
16660     filterValidation : function(e){
16661         if(!e.isNavKeyPress()){
16662             this.validationTask.delay(this.validationDelay);
16663         }
16664     },
16665
16666     // private
16667     onKeyUp : function(e){
16668         if(!e.isNavKeyPress()){
16669             this.autoSize();
16670         }
16671     },
16672     // private - clean the leading white space
16673     cleanLeadingSpace : function(e)
16674     {
16675         if ( this.inputType == 'file') {
16676             return;
16677         }
16678         
16679         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16680     },
16681     /**
16682      * Resets the current field value to the originally-loaded value and clears any validation messages.
16683      *  
16684      */
16685     reset : function(){
16686         Roo.form.TextField.superclass.reset.call(this);
16687        
16688     }, 
16689     // private
16690     preFocus : function(){
16691         
16692         if(this.selectOnFocus){
16693             this.el.dom.select();
16694         }
16695     },
16696
16697     
16698     // private
16699     filterKeys : function(e){
16700         var k = e.getKey();
16701         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16702             return;
16703         }
16704         var c = e.getCharCode(), cc = String.fromCharCode(c);
16705         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16706             return;
16707         }
16708         if(!this.maskRe.test(cc)){
16709             e.stopEvent();
16710         }
16711     },
16712
16713     setValue : function(v){
16714         
16715         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16716         
16717         this.autoSize();
16718     },
16719
16720     /**
16721      * Validates a value according to the field's validation rules and marks the field as invalid
16722      * if the validation fails
16723      * @param {Mixed} value The value to validate
16724      * @return {Boolean} True if the value is valid, else false
16725      */
16726     validateValue : function(value){
16727         if(value.length < 1)  { // if it's blank
16728              if(this.allowBlank){
16729                 this.clearInvalid();
16730                 return true;
16731              }else{
16732                 this.markInvalid(this.blankText);
16733                 return false;
16734              }
16735         }
16736         if(value.length < this.minLength){
16737             this.markInvalid(String.format(this.minLengthText, this.minLength));
16738             return false;
16739         }
16740         if(value.length > this.maxLength){
16741             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16742             return false;
16743         }
16744         if(this.vtype){
16745             var vt = Roo.form.VTypes;
16746             if(!vt[this.vtype](value, this)){
16747                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16748                 return false;
16749             }
16750         }
16751         if(typeof this.validator == "function"){
16752             var msg = this.validator(value);
16753             if(msg !== true){
16754                 this.markInvalid(msg);
16755                 return false;
16756             }
16757         }
16758         if(this.regex && !this.regex.test(value)){
16759             this.markInvalid(this.regexText);
16760             return false;
16761         }
16762         return true;
16763     },
16764
16765     /**
16766      * Selects text in this field
16767      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16768      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16769      */
16770     selectText : function(start, end){
16771         var v = this.getRawValue();
16772         if(v.length > 0){
16773             start = start === undefined ? 0 : start;
16774             end = end === undefined ? v.length : end;
16775             var d = this.el.dom;
16776             if(d.setSelectionRange){
16777                 d.setSelectionRange(start, end);
16778             }else if(d.createTextRange){
16779                 var range = d.createTextRange();
16780                 range.moveStart("character", start);
16781                 range.moveEnd("character", v.length-end);
16782                 range.select();
16783             }
16784         }
16785     },
16786
16787     /**
16788      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16789      * This only takes effect if grow = true, and fires the autosize event.
16790      */
16791     autoSize : function(){
16792         if(!this.grow || !this.rendered){
16793             return;
16794         }
16795         if(!this.metrics){
16796             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16797         }
16798         var el = this.el;
16799         var v = el.dom.value;
16800         var d = document.createElement('div');
16801         d.appendChild(document.createTextNode(v));
16802         v = d.innerHTML;
16803         d = null;
16804         v += "&#160;";
16805         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16806         this.el.setWidth(w);
16807         this.fireEvent("autosize", this, w);
16808     },
16809     
16810     // private
16811     SafariOnKeyDown : function(event)
16812     {
16813         // this is a workaround for a password hang bug on chrome/ webkit.
16814         
16815         var isSelectAll = false;
16816         
16817         if(this.el.dom.selectionEnd > 0){
16818             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16819         }
16820         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16821             event.preventDefault();
16822             this.setValue('');
16823             return;
16824         }
16825         
16826         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16827             
16828             event.preventDefault();
16829             // this is very hacky as keydown always get's upper case.
16830             
16831             var cc = String.fromCharCode(event.getCharCode());
16832             
16833             
16834             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16835             
16836         }
16837         
16838         
16839     }
16840 });/*
16841  * Based on:
16842  * Ext JS Library 1.1.1
16843  * Copyright(c) 2006-2007, Ext JS, LLC.
16844  *
16845  * Originally Released Under LGPL - original licence link has changed is not relivant.
16846  *
16847  * Fork - LGPL
16848  * <script type="text/javascript">
16849  */
16850  
16851 /**
16852  * @class Roo.form.Hidden
16853  * @extends Roo.form.TextField
16854  * Simple Hidden element used on forms 
16855  * 
16856  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16857  * 
16858  * @constructor
16859  * Creates a new Hidden form element.
16860  * @param {Object} config Configuration options
16861  */
16862
16863
16864
16865 // easy hidden field...
16866 Roo.form.Hidden = function(config){
16867     Roo.form.Hidden.superclass.constructor.call(this, config);
16868 };
16869   
16870 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16871     fieldLabel:      '',
16872     inputType:      'hidden',
16873     width:          50,
16874     allowBlank:     true,
16875     labelSeparator: '',
16876     hidden:         true,
16877     itemCls :       'x-form-item-display-none'
16878
16879
16880 });
16881
16882
16883 /*
16884  * Based on:
16885  * Ext JS Library 1.1.1
16886  * Copyright(c) 2006-2007, Ext JS, LLC.
16887  *
16888  * Originally Released Under LGPL - original licence link has changed is not relivant.
16889  *
16890  * Fork - LGPL
16891  * <script type="text/javascript">
16892  */
16893  
16894 /**
16895  * @class Roo.form.TriggerField
16896  * @extends Roo.form.TextField
16897  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16898  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16899  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16900  * for which you can provide a custom implementation.  For example:
16901  * <pre><code>
16902 var trigger = new Roo.form.TriggerField();
16903 trigger.onTriggerClick = myTriggerFn;
16904 trigger.applyTo('my-field');
16905 </code></pre>
16906  *
16907  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16908  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16909  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
16910  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16911  * @constructor
16912  * Create a new TriggerField.
16913  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16914  * to the base TextField)
16915  */
16916 Roo.form.TriggerField = function(config){
16917     this.mimicing = false;
16918     Roo.form.TriggerField.superclass.constructor.call(this, config);
16919 };
16920
16921 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
16922     /**
16923      * @cfg {String} triggerClass A CSS class to apply to the trigger
16924      */
16925     /**
16926      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16927      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
16928      */
16929     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
16930     /**
16931      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
16932      */
16933     hideTrigger:false,
16934
16935     /** @cfg {Boolean} grow @hide */
16936     /** @cfg {Number} growMin @hide */
16937     /** @cfg {Number} growMax @hide */
16938
16939     /**
16940      * @hide 
16941      * @method
16942      */
16943     autoSize: Roo.emptyFn,
16944     // private
16945     monitorTab : true,
16946     // private
16947     deferHeight : true,
16948
16949     
16950     actionMode : 'wrap',
16951     // private
16952     onResize : function(w, h){
16953         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
16954         if(typeof w == 'number'){
16955             var x = w - this.trigger.getWidth();
16956             this.el.setWidth(this.adjustWidth('input', x));
16957             this.trigger.setStyle('left', x+'px');
16958         }
16959     },
16960
16961     // private
16962     adjustSize : Roo.BoxComponent.prototype.adjustSize,
16963
16964     // private
16965     getResizeEl : function(){
16966         return this.wrap;
16967     },
16968
16969     // private
16970     getPositionEl : function(){
16971         return this.wrap;
16972     },
16973
16974     // private
16975     alignErrorIcon : function(){
16976         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
16977     },
16978
16979     // private
16980     onRender : function(ct, position){
16981         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
16982         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
16983         this.trigger = this.wrap.createChild(this.triggerConfig ||
16984                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
16985         if(this.hideTrigger){
16986             this.trigger.setDisplayed(false);
16987         }
16988         this.initTrigger();
16989         if(!this.width){
16990             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
16991         }
16992     },
16993
16994     // private
16995     initTrigger : function(){
16996         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
16997         this.trigger.addClassOnOver('x-form-trigger-over');
16998         this.trigger.addClassOnClick('x-form-trigger-click');
16999     },
17000
17001     // private
17002     onDestroy : function(){
17003         if(this.trigger){
17004             this.trigger.removeAllListeners();
17005             this.trigger.remove();
17006         }
17007         if(this.wrap){
17008             this.wrap.remove();
17009         }
17010         Roo.form.TriggerField.superclass.onDestroy.call(this);
17011     },
17012
17013     // private
17014     onFocus : function(){
17015         Roo.form.TriggerField.superclass.onFocus.call(this);
17016         if(!this.mimicing){
17017             this.wrap.addClass('x-trigger-wrap-focus');
17018             this.mimicing = true;
17019             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17020             if(this.monitorTab){
17021                 this.el.on("keydown", this.checkTab, this);
17022             }
17023         }
17024     },
17025
17026     // private
17027     checkTab : function(e){
17028         if(e.getKey() == e.TAB){
17029             this.triggerBlur();
17030         }
17031     },
17032
17033     // private
17034     onBlur : function(){
17035         // do nothing
17036     },
17037
17038     // private
17039     mimicBlur : function(e, t){
17040         if(!this.wrap.contains(t) && this.validateBlur()){
17041             this.triggerBlur();
17042         }
17043     },
17044
17045     // private
17046     triggerBlur : function(){
17047         this.mimicing = false;
17048         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17049         if(this.monitorTab){
17050             this.el.un("keydown", this.checkTab, this);
17051         }
17052         this.wrap.removeClass('x-trigger-wrap-focus');
17053         Roo.form.TriggerField.superclass.onBlur.call(this);
17054     },
17055
17056     // private
17057     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17058     validateBlur : function(e, t){
17059         return true;
17060     },
17061
17062     // private
17063     onDisable : function(){
17064         Roo.form.TriggerField.superclass.onDisable.call(this);
17065         if(this.wrap){
17066             this.wrap.addClass('x-item-disabled');
17067         }
17068     },
17069
17070     // private
17071     onEnable : function(){
17072         Roo.form.TriggerField.superclass.onEnable.call(this);
17073         if(this.wrap){
17074             this.wrap.removeClass('x-item-disabled');
17075         }
17076     },
17077
17078     // private
17079     onShow : function(){
17080         var ae = this.getActionEl();
17081         
17082         if(ae){
17083             ae.dom.style.display = '';
17084             ae.dom.style.visibility = 'visible';
17085         }
17086     },
17087
17088     // private
17089     
17090     onHide : function(){
17091         var ae = this.getActionEl();
17092         ae.dom.style.display = 'none';
17093     },
17094
17095     /**
17096      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17097      * by an implementing function.
17098      * @method
17099      * @param {EventObject} e
17100      */
17101     onTriggerClick : Roo.emptyFn
17102 });
17103
17104 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17105 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17106 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17107 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17108     initComponent : function(){
17109         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17110
17111         this.triggerConfig = {
17112             tag:'span', cls:'x-form-twin-triggers', cn:[
17113             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17114             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17115         ]};
17116     },
17117
17118     getTrigger : function(index){
17119         return this.triggers[index];
17120     },
17121
17122     initTrigger : function(){
17123         var ts = this.trigger.select('.x-form-trigger', true);
17124         this.wrap.setStyle('overflow', 'hidden');
17125         var triggerField = this;
17126         ts.each(function(t, all, index){
17127             t.hide = function(){
17128                 var w = triggerField.wrap.getWidth();
17129                 this.dom.style.display = 'none';
17130                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17131             };
17132             t.show = function(){
17133                 var w = triggerField.wrap.getWidth();
17134                 this.dom.style.display = '';
17135                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17136             };
17137             var triggerIndex = 'Trigger'+(index+1);
17138
17139             if(this['hide'+triggerIndex]){
17140                 t.dom.style.display = 'none';
17141             }
17142             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17143             t.addClassOnOver('x-form-trigger-over');
17144             t.addClassOnClick('x-form-trigger-click');
17145         }, this);
17146         this.triggers = ts.elements;
17147     },
17148
17149     onTrigger1Click : Roo.emptyFn,
17150     onTrigger2Click : Roo.emptyFn
17151 });/*
17152  * Based on:
17153  * Ext JS Library 1.1.1
17154  * Copyright(c) 2006-2007, Ext JS, LLC.
17155  *
17156  * Originally Released Under LGPL - original licence link has changed is not relivant.
17157  *
17158  * Fork - LGPL
17159  * <script type="text/javascript">
17160  */
17161  
17162 /**
17163  * @class Roo.form.TextArea
17164  * @extends Roo.form.TextField
17165  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17166  * support for auto-sizing.
17167  * @constructor
17168  * Creates a new TextArea
17169  * @param {Object} config Configuration options
17170  */
17171 Roo.form.TextArea = function(config){
17172     Roo.form.TextArea.superclass.constructor.call(this, config);
17173     // these are provided exchanges for backwards compat
17174     // minHeight/maxHeight were replaced by growMin/growMax to be
17175     // compatible with TextField growing config values
17176     if(this.minHeight !== undefined){
17177         this.growMin = this.minHeight;
17178     }
17179     if(this.maxHeight !== undefined){
17180         this.growMax = this.maxHeight;
17181     }
17182 };
17183
17184 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17185     /**
17186      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17187      */
17188     growMin : 60,
17189     /**
17190      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17191      */
17192     growMax: 1000,
17193     /**
17194      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17195      * in the field (equivalent to setting overflow: hidden, defaults to false)
17196      */
17197     preventScrollbars: false,
17198     /**
17199      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17200      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17201      */
17202
17203     // private
17204     onRender : function(ct, position){
17205         if(!this.el){
17206             this.defaultAutoCreate = {
17207                 tag: "textarea",
17208                 style:"width:300px;height:60px;",
17209                 autocomplete: "new-password"
17210             };
17211         }
17212         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17213         if(this.grow){
17214             this.textSizeEl = Roo.DomHelper.append(document.body, {
17215                 tag: "pre", cls: "x-form-grow-sizer"
17216             });
17217             if(this.preventScrollbars){
17218                 this.el.setStyle("overflow", "hidden");
17219             }
17220             this.el.setHeight(this.growMin);
17221         }
17222     },
17223
17224     onDestroy : function(){
17225         if(this.textSizeEl){
17226             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17227         }
17228         Roo.form.TextArea.superclass.onDestroy.call(this);
17229     },
17230
17231     // private
17232     onKeyUp : function(e){
17233         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17234             this.autoSize();
17235         }
17236     },
17237
17238     /**
17239      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17240      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17241      */
17242     autoSize : function(){
17243         if(!this.grow || !this.textSizeEl){
17244             return;
17245         }
17246         var el = this.el;
17247         var v = el.dom.value;
17248         var ts = this.textSizeEl;
17249
17250         ts.innerHTML = '';
17251         ts.appendChild(document.createTextNode(v));
17252         v = ts.innerHTML;
17253
17254         Roo.fly(ts).setWidth(this.el.getWidth());
17255         if(v.length < 1){
17256             v = "&#160;&#160;";
17257         }else{
17258             if(Roo.isIE){
17259                 v = v.replace(/\n/g, '<p>&#160;</p>');
17260             }
17261             v += "&#160;\n&#160;";
17262         }
17263         ts.innerHTML = v;
17264         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17265         if(h != this.lastHeight){
17266             this.lastHeight = h;
17267             this.el.setHeight(h);
17268             this.fireEvent("autosize", this, h);
17269         }
17270     }
17271 });/*
17272  * Based on:
17273  * Ext JS Library 1.1.1
17274  * Copyright(c) 2006-2007, Ext JS, LLC.
17275  *
17276  * Originally Released Under LGPL - original licence link has changed is not relivant.
17277  *
17278  * Fork - LGPL
17279  * <script type="text/javascript">
17280  */
17281  
17282
17283 /**
17284  * @class Roo.form.NumberField
17285  * @extends Roo.form.TextField
17286  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17287  * @constructor
17288  * Creates a new NumberField
17289  * @param {Object} config Configuration options
17290  */
17291 Roo.form.NumberField = function(config){
17292     Roo.form.NumberField.superclass.constructor.call(this, config);
17293 };
17294
17295 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17296     /**
17297      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17298      */
17299     fieldClass: "x-form-field x-form-num-field",
17300     /**
17301      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17302      */
17303     allowDecimals : true,
17304     /**
17305      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17306      */
17307     decimalSeparator : ".",
17308     /**
17309      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17310      */
17311     decimalPrecision : 2,
17312     /**
17313      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17314      */
17315     allowNegative : true,
17316     /**
17317      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17318      */
17319     minValue : Number.NEGATIVE_INFINITY,
17320     /**
17321      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17322      */
17323     maxValue : Number.MAX_VALUE,
17324     /**
17325      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17326      */
17327     minText : "The minimum value for this field is {0}",
17328     /**
17329      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17330      */
17331     maxText : "The maximum value for this field is {0}",
17332     /**
17333      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17334      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17335      */
17336     nanText : "{0} is not a valid number",
17337
17338     // private
17339     initEvents : function(){
17340         Roo.form.NumberField.superclass.initEvents.call(this);
17341         var allowed = "0123456789";
17342         if(this.allowDecimals){
17343             allowed += this.decimalSeparator;
17344         }
17345         if(this.allowNegative){
17346             allowed += "-";
17347         }
17348         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17349         var keyPress = function(e){
17350             var k = e.getKey();
17351             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17352                 return;
17353             }
17354             var c = e.getCharCode();
17355             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17356                 e.stopEvent();
17357             }
17358         };
17359         this.el.on("keypress", keyPress, this);
17360     },
17361
17362     // private
17363     validateValue : function(value){
17364         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17365             return false;
17366         }
17367         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17368              return true;
17369         }
17370         var num = this.parseValue(value);
17371         if(isNaN(num)){
17372             this.markInvalid(String.format(this.nanText, value));
17373             return false;
17374         }
17375         if(num < this.minValue){
17376             this.markInvalid(String.format(this.minText, this.minValue));
17377             return false;
17378         }
17379         if(num > this.maxValue){
17380             this.markInvalid(String.format(this.maxText, this.maxValue));
17381             return false;
17382         }
17383         return true;
17384     },
17385
17386     getValue : function(){
17387         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17388     },
17389
17390     // private
17391     parseValue : function(value){
17392         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17393         return isNaN(value) ? '' : value;
17394     },
17395
17396     // private
17397     fixPrecision : function(value){
17398         var nan = isNaN(value);
17399         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17400             return nan ? '' : value;
17401         }
17402         return parseFloat(value).toFixed(this.decimalPrecision);
17403     },
17404
17405     setValue : function(v){
17406         v = this.fixPrecision(v);
17407         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17408     },
17409
17410     // private
17411     decimalPrecisionFcn : function(v){
17412         return Math.floor(v);
17413     },
17414
17415     beforeBlur : function(){
17416         var v = this.parseValue(this.getRawValue());
17417         if(v){
17418             this.setValue(v);
17419         }
17420     }
17421 });/*
17422  * Based on:
17423  * Ext JS Library 1.1.1
17424  * Copyright(c) 2006-2007, Ext JS, LLC.
17425  *
17426  * Originally Released Under LGPL - original licence link has changed is not relivant.
17427  *
17428  * Fork - LGPL
17429  * <script type="text/javascript">
17430  */
17431  
17432 /**
17433  * @class Roo.form.DateField
17434  * @extends Roo.form.TriggerField
17435  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17436 * @constructor
17437 * Create a new DateField
17438 * @param {Object} config
17439  */
17440 Roo.form.DateField = function(config)
17441 {
17442     Roo.form.DateField.superclass.constructor.call(this, config);
17443     
17444       this.addEvents({
17445          
17446         /**
17447          * @event select
17448          * Fires when a date is selected
17449              * @param {Roo.form.DateField} combo This combo box
17450              * @param {Date} date The date selected
17451              */
17452         'select' : true
17453          
17454     });
17455     
17456     
17457     if(typeof this.minValue == "string") {
17458         this.minValue = this.parseDate(this.minValue);
17459     }
17460     if(typeof this.maxValue == "string") {
17461         this.maxValue = this.parseDate(this.maxValue);
17462     }
17463     this.ddMatch = null;
17464     if(this.disabledDates){
17465         var dd = this.disabledDates;
17466         var re = "(?:";
17467         for(var i = 0; i < dd.length; i++){
17468             re += dd[i];
17469             if(i != dd.length-1) {
17470                 re += "|";
17471             }
17472         }
17473         this.ddMatch = new RegExp(re + ")");
17474     }
17475 };
17476
17477 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17478     /**
17479      * @cfg {String} format
17480      * The default date format string which can be overriden for localization support.  The format must be
17481      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17482      */
17483     format : "m/d/y",
17484     /**
17485      * @cfg {String} altFormats
17486      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17487      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17488      */
17489     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17490     /**
17491      * @cfg {Array} disabledDays
17492      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17493      */
17494     disabledDays : null,
17495     /**
17496      * @cfg {String} disabledDaysText
17497      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17498      */
17499     disabledDaysText : "Disabled",
17500     /**
17501      * @cfg {Array} disabledDates
17502      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17503      * expression so they are very powerful. Some examples:
17504      * <ul>
17505      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17506      * <li>["03/08", "09/16"] would disable those days for every year</li>
17507      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17508      * <li>["03/../2006"] would disable every day in March 2006</li>
17509      * <li>["^03"] would disable every day in every March</li>
17510      * </ul>
17511      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17512      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17513      */
17514     disabledDates : null,
17515     /**
17516      * @cfg {String} disabledDatesText
17517      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17518      */
17519     disabledDatesText : "Disabled",
17520     /**
17521      * @cfg {Date/String} minValue
17522      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17523      * valid format (defaults to null).
17524      */
17525     minValue : null,
17526     /**
17527      * @cfg {Date/String} maxValue
17528      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17529      * valid format (defaults to null).
17530      */
17531     maxValue : null,
17532     /**
17533      * @cfg {String} minText
17534      * The error text to display when the date in the cell is before minValue (defaults to
17535      * 'The date in this field must be after {minValue}').
17536      */
17537     minText : "The date in this field must be equal to or after {0}",
17538     /**
17539      * @cfg {String} maxText
17540      * The error text to display when the date in the cell is after maxValue (defaults to
17541      * 'The date in this field must be before {maxValue}').
17542      */
17543     maxText : "The date in this field must be equal to or before {0}",
17544     /**
17545      * @cfg {String} invalidText
17546      * The error text to display when the date in the field is invalid (defaults to
17547      * '{value} is not a valid date - it must be in the format {format}').
17548      */
17549     invalidText : "{0} is not a valid date - it must be in the format {1}",
17550     /**
17551      * @cfg {String} triggerClass
17552      * An additional CSS class used to style the trigger button.  The trigger will always get the
17553      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17554      * which displays a calendar icon).
17555      */
17556     triggerClass : 'x-form-date-trigger',
17557     
17558
17559     /**
17560      * @cfg {Boolean} useIso
17561      * if enabled, then the date field will use a hidden field to store the 
17562      * real value as iso formated date. default (false)
17563      */ 
17564     useIso : false,
17565     /**
17566      * @cfg {String/Object} autoCreate
17567      * A DomHelper element spec, or true for a default element spec (defaults to
17568      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17569      */ 
17570     // private
17571     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17572     
17573     // private
17574     hiddenField: false,
17575     
17576     onRender : function(ct, position)
17577     {
17578         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17579         if (this.useIso) {
17580             //this.el.dom.removeAttribute('name'); 
17581             Roo.log("Changing name?");
17582             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17583             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17584                     'before', true);
17585             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17586             // prevent input submission
17587             this.hiddenName = this.name;
17588         }
17589             
17590             
17591     },
17592     
17593     // private
17594     validateValue : function(value)
17595     {
17596         value = this.formatDate(value);
17597         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17598             Roo.log('super failed');
17599             return false;
17600         }
17601         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17602              return true;
17603         }
17604         var svalue = value;
17605         value = this.parseDate(value);
17606         if(!value){
17607             Roo.log('parse date failed' + svalue);
17608             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17609             return false;
17610         }
17611         var time = value.getTime();
17612         if(this.minValue && time < this.minValue.getTime()){
17613             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17614             return false;
17615         }
17616         if(this.maxValue && time > this.maxValue.getTime()){
17617             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17618             return false;
17619         }
17620         if(this.disabledDays){
17621             var day = value.getDay();
17622             for(var i = 0; i < this.disabledDays.length; i++) {
17623                 if(day === this.disabledDays[i]){
17624                     this.markInvalid(this.disabledDaysText);
17625                     return false;
17626                 }
17627             }
17628         }
17629         var fvalue = this.formatDate(value);
17630         if(this.ddMatch && this.ddMatch.test(fvalue)){
17631             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17632             return false;
17633         }
17634         return true;
17635     },
17636
17637     // private
17638     // Provides logic to override the default TriggerField.validateBlur which just returns true
17639     validateBlur : function(){
17640         return !this.menu || !this.menu.isVisible();
17641     },
17642     
17643     getName: function()
17644     {
17645         // returns hidden if it's set..
17646         if (!this.rendered) {return ''};
17647         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17648         
17649     },
17650
17651     /**
17652      * Returns the current date value of the date field.
17653      * @return {Date} The date value
17654      */
17655     getValue : function(){
17656         
17657         return  this.hiddenField ?
17658                 this.hiddenField.value :
17659                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17660     },
17661
17662     /**
17663      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17664      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17665      * (the default format used is "m/d/y").
17666      * <br />Usage:
17667      * <pre><code>
17668 //All of these calls set the same date value (May 4, 2006)
17669
17670 //Pass a date object:
17671 var dt = new Date('5/4/06');
17672 dateField.setValue(dt);
17673
17674 //Pass a date string (default format):
17675 dateField.setValue('5/4/06');
17676
17677 //Pass a date string (custom format):
17678 dateField.format = 'Y-m-d';
17679 dateField.setValue('2006-5-4');
17680 </code></pre>
17681      * @param {String/Date} date The date or valid date string
17682      */
17683     setValue : function(date){
17684         if (this.hiddenField) {
17685             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17686         }
17687         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17688         // make sure the value field is always stored as a date..
17689         this.value = this.parseDate(date);
17690         
17691         
17692     },
17693
17694     // private
17695     parseDate : function(value){
17696         if(!value || value instanceof Date){
17697             return value;
17698         }
17699         var v = Date.parseDate(value, this.format);
17700          if (!v && this.useIso) {
17701             v = Date.parseDate(value, 'Y-m-d');
17702         }
17703         if(!v && this.altFormats){
17704             if(!this.altFormatsArray){
17705                 this.altFormatsArray = this.altFormats.split("|");
17706             }
17707             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17708                 v = Date.parseDate(value, this.altFormatsArray[i]);
17709             }
17710         }
17711         return v;
17712     },
17713
17714     // private
17715     formatDate : function(date, fmt){
17716         return (!date || !(date instanceof Date)) ?
17717                date : date.dateFormat(fmt || this.format);
17718     },
17719
17720     // private
17721     menuListeners : {
17722         select: function(m, d){
17723             
17724             this.setValue(d);
17725             this.fireEvent('select', this, d);
17726         },
17727         show : function(){ // retain focus styling
17728             this.onFocus();
17729         },
17730         hide : function(){
17731             this.focus.defer(10, this);
17732             var ml = this.menuListeners;
17733             this.menu.un("select", ml.select,  this);
17734             this.menu.un("show", ml.show,  this);
17735             this.menu.un("hide", ml.hide,  this);
17736         }
17737     },
17738
17739     // private
17740     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17741     onTriggerClick : function(){
17742         if(this.disabled){
17743             return;
17744         }
17745         if(this.menu == null){
17746             this.menu = new Roo.menu.DateMenu();
17747         }
17748         Roo.apply(this.menu.picker,  {
17749             showClear: this.allowBlank,
17750             minDate : this.minValue,
17751             maxDate : this.maxValue,
17752             disabledDatesRE : this.ddMatch,
17753             disabledDatesText : this.disabledDatesText,
17754             disabledDays : this.disabledDays,
17755             disabledDaysText : this.disabledDaysText,
17756             format : this.useIso ? 'Y-m-d' : this.format,
17757             minText : String.format(this.minText, this.formatDate(this.minValue)),
17758             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17759         });
17760         this.menu.on(Roo.apply({}, this.menuListeners, {
17761             scope:this
17762         }));
17763         this.menu.picker.setValue(this.getValue() || new Date());
17764         this.menu.show(this.el, "tl-bl?");
17765     },
17766
17767     beforeBlur : function(){
17768         var v = this.parseDate(this.getRawValue());
17769         if(v){
17770             this.setValue(v);
17771         }
17772     },
17773
17774     /*@
17775      * overide
17776      * 
17777      */
17778     isDirty : function() {
17779         if(this.disabled) {
17780             return false;
17781         }
17782         
17783         if(typeof(this.startValue) === 'undefined'){
17784             return false;
17785         }
17786         
17787         return String(this.getValue()) !== String(this.startValue);
17788         
17789     },
17790     // @overide
17791     cleanLeadingSpace : function(e)
17792     {
17793        return;
17794     }
17795     
17796 });/*
17797  * Based on:
17798  * Ext JS Library 1.1.1
17799  * Copyright(c) 2006-2007, Ext JS, LLC.
17800  *
17801  * Originally Released Under LGPL - original licence link has changed is not relivant.
17802  *
17803  * Fork - LGPL
17804  * <script type="text/javascript">
17805  */
17806  
17807 /**
17808  * @class Roo.form.MonthField
17809  * @extends Roo.form.TriggerField
17810  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17811 * @constructor
17812 * Create a new MonthField
17813 * @param {Object} config
17814  */
17815 Roo.form.MonthField = function(config){
17816     
17817     Roo.form.MonthField.superclass.constructor.call(this, config);
17818     
17819       this.addEvents({
17820          
17821         /**
17822          * @event select
17823          * Fires when a date is selected
17824              * @param {Roo.form.MonthFieeld} combo This combo box
17825              * @param {Date} date The date selected
17826              */
17827         'select' : true
17828          
17829     });
17830     
17831     
17832     if(typeof this.minValue == "string") {
17833         this.minValue = this.parseDate(this.minValue);
17834     }
17835     if(typeof this.maxValue == "string") {
17836         this.maxValue = this.parseDate(this.maxValue);
17837     }
17838     this.ddMatch = null;
17839     if(this.disabledDates){
17840         var dd = this.disabledDates;
17841         var re = "(?:";
17842         for(var i = 0; i < dd.length; i++){
17843             re += dd[i];
17844             if(i != dd.length-1) {
17845                 re += "|";
17846             }
17847         }
17848         this.ddMatch = new RegExp(re + ")");
17849     }
17850 };
17851
17852 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17853     /**
17854      * @cfg {String} format
17855      * The default date format string which can be overriden for localization support.  The format must be
17856      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17857      */
17858     format : "M Y",
17859     /**
17860      * @cfg {String} altFormats
17861      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17862      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17863      */
17864     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17865     /**
17866      * @cfg {Array} disabledDays
17867      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17868      */
17869     disabledDays : [0,1,2,3,4,5,6],
17870     /**
17871      * @cfg {String} disabledDaysText
17872      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17873      */
17874     disabledDaysText : "Disabled",
17875     /**
17876      * @cfg {Array} disabledDates
17877      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17878      * expression so they are very powerful. Some examples:
17879      * <ul>
17880      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17881      * <li>["03/08", "09/16"] would disable those days for every year</li>
17882      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17883      * <li>["03/../2006"] would disable every day in March 2006</li>
17884      * <li>["^03"] would disable every day in every March</li>
17885      * </ul>
17886      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17887      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17888      */
17889     disabledDates : null,
17890     /**
17891      * @cfg {String} disabledDatesText
17892      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17893      */
17894     disabledDatesText : "Disabled",
17895     /**
17896      * @cfg {Date/String} minValue
17897      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17898      * valid format (defaults to null).
17899      */
17900     minValue : null,
17901     /**
17902      * @cfg {Date/String} maxValue
17903      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17904      * valid format (defaults to null).
17905      */
17906     maxValue : null,
17907     /**
17908      * @cfg {String} minText
17909      * The error text to display when the date in the cell is before minValue (defaults to
17910      * 'The date in this field must be after {minValue}').
17911      */
17912     minText : "The date in this field must be equal to or after {0}",
17913     /**
17914      * @cfg {String} maxTextf
17915      * The error text to display when the date in the cell is after maxValue (defaults to
17916      * 'The date in this field must be before {maxValue}').
17917      */
17918     maxText : "The date in this field must be equal to or before {0}",
17919     /**
17920      * @cfg {String} invalidText
17921      * The error text to display when the date in the field is invalid (defaults to
17922      * '{value} is not a valid date - it must be in the format {format}').
17923      */
17924     invalidText : "{0} is not a valid date - it must be in the format {1}",
17925     /**
17926      * @cfg {String} triggerClass
17927      * An additional CSS class used to style the trigger button.  The trigger will always get the
17928      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17929      * which displays a calendar icon).
17930      */
17931     triggerClass : 'x-form-date-trigger',
17932     
17933
17934     /**
17935      * @cfg {Boolean} useIso
17936      * if enabled, then the date field will use a hidden field to store the 
17937      * real value as iso formated date. default (true)
17938      */ 
17939     useIso : true,
17940     /**
17941      * @cfg {String/Object} autoCreate
17942      * A DomHelper element spec, or true for a default element spec (defaults to
17943      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17944      */ 
17945     // private
17946     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
17947     
17948     // private
17949     hiddenField: false,
17950     
17951     hideMonthPicker : false,
17952     
17953     onRender : function(ct, position)
17954     {
17955         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
17956         if (this.useIso) {
17957             this.el.dom.removeAttribute('name'); 
17958             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17959                     'before', true);
17960             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17961             // prevent input submission
17962             this.hiddenName = this.name;
17963         }
17964             
17965             
17966     },
17967     
17968     // private
17969     validateValue : function(value)
17970     {
17971         value = this.formatDate(value);
17972         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
17973             return false;
17974         }
17975         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17976              return true;
17977         }
17978         var svalue = value;
17979         value = this.parseDate(value);
17980         if(!value){
17981             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17982             return false;
17983         }
17984         var time = value.getTime();
17985         if(this.minValue && time < this.minValue.getTime()){
17986             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17987             return false;
17988         }
17989         if(this.maxValue && time > this.maxValue.getTime()){
17990             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17991             return false;
17992         }
17993         /*if(this.disabledDays){
17994             var day = value.getDay();
17995             for(var i = 0; i < this.disabledDays.length; i++) {
17996                 if(day === this.disabledDays[i]){
17997                     this.markInvalid(this.disabledDaysText);
17998                     return false;
17999                 }
18000             }
18001         }
18002         */
18003         var fvalue = this.formatDate(value);
18004         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18005             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18006             return false;
18007         }
18008         */
18009         return true;
18010     },
18011
18012     // private
18013     // Provides logic to override the default TriggerField.validateBlur which just returns true
18014     validateBlur : function(){
18015         return !this.menu || !this.menu.isVisible();
18016     },
18017
18018     /**
18019      * Returns the current date value of the date field.
18020      * @return {Date} The date value
18021      */
18022     getValue : function(){
18023         
18024         
18025         
18026         return  this.hiddenField ?
18027                 this.hiddenField.value :
18028                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18029     },
18030
18031     /**
18032      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18033      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18034      * (the default format used is "m/d/y").
18035      * <br />Usage:
18036      * <pre><code>
18037 //All of these calls set the same date value (May 4, 2006)
18038
18039 //Pass a date object:
18040 var dt = new Date('5/4/06');
18041 monthField.setValue(dt);
18042
18043 //Pass a date string (default format):
18044 monthField.setValue('5/4/06');
18045
18046 //Pass a date string (custom format):
18047 monthField.format = 'Y-m-d';
18048 monthField.setValue('2006-5-4');
18049 </code></pre>
18050      * @param {String/Date} date The date or valid date string
18051      */
18052     setValue : function(date){
18053         Roo.log('month setValue' + date);
18054         // can only be first of month..
18055         
18056         var val = this.parseDate(date);
18057         
18058         if (this.hiddenField) {
18059             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18060         }
18061         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18062         this.value = this.parseDate(date);
18063     },
18064
18065     // private
18066     parseDate : function(value){
18067         if(!value || value instanceof Date){
18068             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18069             return value;
18070         }
18071         var v = Date.parseDate(value, this.format);
18072         if (!v && this.useIso) {
18073             v = Date.parseDate(value, 'Y-m-d');
18074         }
18075         if (v) {
18076             // 
18077             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18078         }
18079         
18080         
18081         if(!v && this.altFormats){
18082             if(!this.altFormatsArray){
18083                 this.altFormatsArray = this.altFormats.split("|");
18084             }
18085             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18086                 v = Date.parseDate(value, this.altFormatsArray[i]);
18087             }
18088         }
18089         return v;
18090     },
18091
18092     // private
18093     formatDate : function(date, fmt){
18094         return (!date || !(date instanceof Date)) ?
18095                date : date.dateFormat(fmt || this.format);
18096     },
18097
18098     // private
18099     menuListeners : {
18100         select: function(m, d){
18101             this.setValue(d);
18102             this.fireEvent('select', this, d);
18103         },
18104         show : function(){ // retain focus styling
18105             this.onFocus();
18106         },
18107         hide : function(){
18108             this.focus.defer(10, this);
18109             var ml = this.menuListeners;
18110             this.menu.un("select", ml.select,  this);
18111             this.menu.un("show", ml.show,  this);
18112             this.menu.un("hide", ml.hide,  this);
18113         }
18114     },
18115     // private
18116     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18117     onTriggerClick : function(){
18118         if(this.disabled){
18119             return;
18120         }
18121         if(this.menu == null){
18122             this.menu = new Roo.menu.DateMenu();
18123            
18124         }
18125         
18126         Roo.apply(this.menu.picker,  {
18127             
18128             showClear: this.allowBlank,
18129             minDate : this.minValue,
18130             maxDate : this.maxValue,
18131             disabledDatesRE : this.ddMatch,
18132             disabledDatesText : this.disabledDatesText,
18133             
18134             format : this.useIso ? 'Y-m-d' : this.format,
18135             minText : String.format(this.minText, this.formatDate(this.minValue)),
18136             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18137             
18138         });
18139          this.menu.on(Roo.apply({}, this.menuListeners, {
18140             scope:this
18141         }));
18142        
18143         
18144         var m = this.menu;
18145         var p = m.picker;
18146         
18147         // hide month picker get's called when we called by 'before hide';
18148         
18149         var ignorehide = true;
18150         p.hideMonthPicker  = function(disableAnim){
18151             if (ignorehide) {
18152                 return;
18153             }
18154              if(this.monthPicker){
18155                 Roo.log("hideMonthPicker called");
18156                 if(disableAnim === true){
18157                     this.monthPicker.hide();
18158                 }else{
18159                     this.monthPicker.slideOut('t', {duration:.2});
18160                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18161                     p.fireEvent("select", this, this.value);
18162                     m.hide();
18163                 }
18164             }
18165         }
18166         
18167         Roo.log('picker set value');
18168         Roo.log(this.getValue());
18169         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18170         m.show(this.el, 'tl-bl?');
18171         ignorehide  = false;
18172         // this will trigger hideMonthPicker..
18173         
18174         
18175         // hidden the day picker
18176         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18177         
18178         
18179         
18180       
18181         
18182         p.showMonthPicker.defer(100, p);
18183     
18184         
18185        
18186     },
18187
18188     beforeBlur : function(){
18189         var v = this.parseDate(this.getRawValue());
18190         if(v){
18191             this.setValue(v);
18192         }
18193     }
18194
18195     /** @cfg {Boolean} grow @hide */
18196     /** @cfg {Number} growMin @hide */
18197     /** @cfg {Number} growMax @hide */
18198     /**
18199      * @hide
18200      * @method autoSize
18201      */
18202 });/*
18203  * Based on:
18204  * Ext JS Library 1.1.1
18205  * Copyright(c) 2006-2007, Ext JS, LLC.
18206  *
18207  * Originally Released Under LGPL - original licence link has changed is not relivant.
18208  *
18209  * Fork - LGPL
18210  * <script type="text/javascript">
18211  */
18212  
18213
18214 /**
18215  * @class Roo.form.ComboBox
18216  * @extends Roo.form.TriggerField
18217  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18218  * @constructor
18219  * Create a new ComboBox.
18220  * @param {Object} config Configuration options
18221  */
18222 Roo.form.ComboBox = function(config){
18223     Roo.form.ComboBox.superclass.constructor.call(this, config);
18224     this.addEvents({
18225         /**
18226          * @event expand
18227          * Fires when the dropdown list is expanded
18228              * @param {Roo.form.ComboBox} combo This combo box
18229              */
18230         'expand' : true,
18231         /**
18232          * @event collapse
18233          * Fires when the dropdown list is collapsed
18234              * @param {Roo.form.ComboBox} combo This combo box
18235              */
18236         'collapse' : true,
18237         /**
18238          * @event beforeselect
18239          * Fires before a list item is selected. Return false to cancel the selection.
18240              * @param {Roo.form.ComboBox} combo This combo box
18241              * @param {Roo.data.Record} record The data record returned from the underlying store
18242              * @param {Number} index The index of the selected item in the dropdown list
18243              */
18244         'beforeselect' : true,
18245         /**
18246          * @event select
18247          * Fires when a list item is selected
18248              * @param {Roo.form.ComboBox} combo This combo box
18249              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18250              * @param {Number} index The index of the selected item in the dropdown list
18251              */
18252         'select' : true,
18253         /**
18254          * @event beforequery
18255          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18256          * The event object passed has these properties:
18257              * @param {Roo.form.ComboBox} combo This combo box
18258              * @param {String} query The query
18259              * @param {Boolean} forceAll true to force "all" query
18260              * @param {Boolean} cancel true to cancel the query
18261              * @param {Object} e The query event object
18262              */
18263         'beforequery': true,
18264          /**
18265          * @event add
18266          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18267              * @param {Roo.form.ComboBox} combo This combo box
18268              */
18269         'add' : true,
18270         /**
18271          * @event edit
18272          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18273              * @param {Roo.form.ComboBox} combo This combo box
18274              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18275              */
18276         'edit' : true
18277         
18278         
18279     });
18280     if(this.transform){
18281         this.allowDomMove = false;
18282         var s = Roo.getDom(this.transform);
18283         if(!this.hiddenName){
18284             this.hiddenName = s.name;
18285         }
18286         if(!this.store){
18287             this.mode = 'local';
18288             var d = [], opts = s.options;
18289             for(var i = 0, len = opts.length;i < len; i++){
18290                 var o = opts[i];
18291                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18292                 if(o.selected) {
18293                     this.value = value;
18294                 }
18295                 d.push([value, o.text]);
18296             }
18297             this.store = new Roo.data.SimpleStore({
18298                 'id': 0,
18299                 fields: ['value', 'text'],
18300                 data : d
18301             });
18302             this.valueField = 'value';
18303             this.displayField = 'text';
18304         }
18305         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18306         if(!this.lazyRender){
18307             this.target = true;
18308             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18309             s.parentNode.removeChild(s); // remove it
18310             this.render(this.el.parentNode);
18311         }else{
18312             s.parentNode.removeChild(s); // remove it
18313         }
18314
18315     }
18316     if (this.store) {
18317         this.store = Roo.factory(this.store, Roo.data);
18318     }
18319     
18320     this.selectedIndex = -1;
18321     if(this.mode == 'local'){
18322         if(config.queryDelay === undefined){
18323             this.queryDelay = 10;
18324         }
18325         if(config.minChars === undefined){
18326             this.minChars = 0;
18327         }
18328     }
18329 };
18330
18331 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18332     /**
18333      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18334      */
18335     /**
18336      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18337      * rendering into an Roo.Editor, defaults to false)
18338      */
18339     /**
18340      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18341      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18342      */
18343     /**
18344      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18345      */
18346     /**
18347      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18348      * the dropdown list (defaults to undefined, with no header element)
18349      */
18350
18351      /**
18352      * @cfg {String/Roo.Template} tpl The template to use to render the output
18353      */
18354      
18355     // private
18356     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18357     /**
18358      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18359      */
18360     listWidth: undefined,
18361     /**
18362      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18363      * mode = 'remote' or 'text' if mode = 'local')
18364      */
18365     displayField: undefined,
18366     /**
18367      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18368      * mode = 'remote' or 'value' if mode = 'local'). 
18369      * Note: use of a valueField requires the user make a selection
18370      * in order for a value to be mapped.
18371      */
18372     valueField: undefined,
18373     
18374     
18375     /**
18376      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18377      * field's data value (defaults to the underlying DOM element's name)
18378      */
18379     hiddenName: undefined,
18380     /**
18381      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18382      */
18383     listClass: '',
18384     /**
18385      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18386      */
18387     selectedClass: 'x-combo-selected',
18388     /**
18389      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18390      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18391      * which displays a downward arrow icon).
18392      */
18393     triggerClass : 'x-form-arrow-trigger',
18394     /**
18395      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18396      */
18397     shadow:'sides',
18398     /**
18399      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18400      * anchor positions (defaults to 'tl-bl')
18401      */
18402     listAlign: 'tl-bl?',
18403     /**
18404      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18405      */
18406     maxHeight: 300,
18407     /**
18408      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18409      * query specified by the allQuery config option (defaults to 'query')
18410      */
18411     triggerAction: 'query',
18412     /**
18413      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18414      * (defaults to 4, does not apply if editable = false)
18415      */
18416     minChars : 4,
18417     /**
18418      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18419      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18420      */
18421     typeAhead: false,
18422     /**
18423      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18424      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18425      */
18426     queryDelay: 500,
18427     /**
18428      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18429      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18430      */
18431     pageSize: 0,
18432     /**
18433      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18434      * when editable = true (defaults to false)
18435      */
18436     selectOnFocus:false,
18437     /**
18438      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18439      */
18440     queryParam: 'query',
18441     /**
18442      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18443      * when mode = 'remote' (defaults to 'Loading...')
18444      */
18445     loadingText: 'Loading...',
18446     /**
18447      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18448      */
18449     resizable: false,
18450     /**
18451      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18452      */
18453     handleHeight : 8,
18454     /**
18455      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18456      * traditional select (defaults to true)
18457      */
18458     editable: true,
18459     /**
18460      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18461      */
18462     allQuery: '',
18463     /**
18464      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18465      */
18466     mode: 'remote',
18467     /**
18468      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18469      * listWidth has a higher value)
18470      */
18471     minListWidth : 70,
18472     /**
18473      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18474      * allow the user to set arbitrary text into the field (defaults to false)
18475      */
18476     forceSelection:false,
18477     /**
18478      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18479      * if typeAhead = true (defaults to 250)
18480      */
18481     typeAheadDelay : 250,
18482     /**
18483      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18484      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18485      */
18486     valueNotFoundText : undefined,
18487     /**
18488      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18489      */
18490     blockFocus : false,
18491     
18492     /**
18493      * @cfg {Boolean} disableClear Disable showing of clear button.
18494      */
18495     disableClear : false,
18496     /**
18497      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18498      */
18499     alwaysQuery : false,
18500     
18501     //private
18502     addicon : false,
18503     editicon: false,
18504     
18505     // element that contains real text value.. (when hidden is used..)
18506      
18507     // private
18508     onRender : function(ct, position)
18509     {
18510         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18511         
18512         if(this.hiddenName){
18513             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18514                     'before', true);
18515             this.hiddenField.value =
18516                 this.hiddenValue !== undefined ? this.hiddenValue :
18517                 this.value !== undefined ? this.value : '';
18518
18519             // prevent input submission
18520             this.el.dom.removeAttribute('name');
18521              
18522              
18523         }
18524         
18525         if(Roo.isGecko){
18526             this.el.dom.setAttribute('autocomplete', 'off');
18527         }
18528
18529         var cls = 'x-combo-list';
18530
18531         this.list = new Roo.Layer({
18532             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18533         });
18534
18535         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18536         this.list.setWidth(lw);
18537         this.list.swallowEvent('mousewheel');
18538         this.assetHeight = 0;
18539
18540         if(this.title){
18541             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18542             this.assetHeight += this.header.getHeight();
18543         }
18544
18545         this.innerList = this.list.createChild({cls:cls+'-inner'});
18546         this.innerList.on('mouseover', this.onViewOver, this);
18547         this.innerList.on('mousemove', this.onViewMove, this);
18548         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18549         
18550         if(this.allowBlank && !this.pageSize && !this.disableClear){
18551             this.footer = this.list.createChild({cls:cls+'-ft'});
18552             this.pageTb = new Roo.Toolbar(this.footer);
18553            
18554         }
18555         if(this.pageSize){
18556             this.footer = this.list.createChild({cls:cls+'-ft'});
18557             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18558                     {pageSize: this.pageSize});
18559             
18560         }
18561         
18562         if (this.pageTb && this.allowBlank && !this.disableClear) {
18563             var _this = this;
18564             this.pageTb.add(new Roo.Toolbar.Fill(), {
18565                 cls: 'x-btn-icon x-btn-clear',
18566                 text: '&#160;',
18567                 handler: function()
18568                 {
18569                     _this.collapse();
18570                     _this.clearValue();
18571                     _this.onSelect(false, -1);
18572                 }
18573             });
18574         }
18575         if (this.footer) {
18576             this.assetHeight += this.footer.getHeight();
18577         }
18578         
18579
18580         if(!this.tpl){
18581             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18582         }
18583
18584         this.view = new Roo.View(this.innerList, this.tpl, {
18585             singleSelect:true,
18586             store: this.store,
18587             selectedClass: this.selectedClass
18588         });
18589
18590         this.view.on('click', this.onViewClick, this);
18591
18592         this.store.on('beforeload', this.onBeforeLoad, this);
18593         this.store.on('load', this.onLoad, this);
18594         this.store.on('loadexception', this.onLoadException, this);
18595
18596         if(this.resizable){
18597             this.resizer = new Roo.Resizable(this.list,  {
18598                pinned:true, handles:'se'
18599             });
18600             this.resizer.on('resize', function(r, w, h){
18601                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18602                 this.listWidth = w;
18603                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18604                 this.restrictHeight();
18605             }, this);
18606             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18607         }
18608         if(!this.editable){
18609             this.editable = true;
18610             this.setEditable(false);
18611         }  
18612         
18613         
18614         if (typeof(this.events.add.listeners) != 'undefined') {
18615             
18616             this.addicon = this.wrap.createChild(
18617                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18618        
18619             this.addicon.on('click', function(e) {
18620                 this.fireEvent('add', this);
18621             }, this);
18622         }
18623         if (typeof(this.events.edit.listeners) != 'undefined') {
18624             
18625             this.editicon = this.wrap.createChild(
18626                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18627             if (this.addicon) {
18628                 this.editicon.setStyle('margin-left', '40px');
18629             }
18630             this.editicon.on('click', function(e) {
18631                 
18632                 // we fire even  if inothing is selected..
18633                 this.fireEvent('edit', this, this.lastData );
18634                 
18635             }, this);
18636         }
18637         
18638         
18639         
18640     },
18641
18642     // private
18643     initEvents : function(){
18644         Roo.form.ComboBox.superclass.initEvents.call(this);
18645
18646         this.keyNav = new Roo.KeyNav(this.el, {
18647             "up" : function(e){
18648                 this.inKeyMode = true;
18649                 this.selectPrev();
18650             },
18651
18652             "down" : function(e){
18653                 if(!this.isExpanded()){
18654                     this.onTriggerClick();
18655                 }else{
18656                     this.inKeyMode = true;
18657                     this.selectNext();
18658                 }
18659             },
18660
18661             "enter" : function(e){
18662                 this.onViewClick();
18663                 //return true;
18664             },
18665
18666             "esc" : function(e){
18667                 this.collapse();
18668             },
18669
18670             "tab" : function(e){
18671                 this.onViewClick(false);
18672                 this.fireEvent("specialkey", this, e);
18673                 return true;
18674             },
18675
18676             scope : this,
18677
18678             doRelay : function(foo, bar, hname){
18679                 if(hname == 'down' || this.scope.isExpanded()){
18680                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18681                 }
18682                 return true;
18683             },
18684
18685             forceKeyDown: true
18686         });
18687         this.queryDelay = Math.max(this.queryDelay || 10,
18688                 this.mode == 'local' ? 10 : 250);
18689         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18690         if(this.typeAhead){
18691             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18692         }
18693         if(this.editable !== false){
18694             this.el.on("keyup", this.onKeyUp, this);
18695         }
18696         if(this.forceSelection){
18697             this.on('blur', this.doForce, this);
18698         }
18699     },
18700
18701     onDestroy : function(){
18702         if(this.view){
18703             this.view.setStore(null);
18704             this.view.el.removeAllListeners();
18705             this.view.el.remove();
18706             this.view.purgeListeners();
18707         }
18708         if(this.list){
18709             this.list.destroy();
18710         }
18711         if(this.store){
18712             this.store.un('beforeload', this.onBeforeLoad, this);
18713             this.store.un('load', this.onLoad, this);
18714             this.store.un('loadexception', this.onLoadException, this);
18715         }
18716         Roo.form.ComboBox.superclass.onDestroy.call(this);
18717     },
18718
18719     // private
18720     fireKey : function(e){
18721         if(e.isNavKeyPress() && !this.list.isVisible()){
18722             this.fireEvent("specialkey", this, e);
18723         }
18724     },
18725
18726     // private
18727     onResize: function(w, h){
18728         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18729         
18730         if(typeof w != 'number'){
18731             // we do not handle it!?!?
18732             return;
18733         }
18734         var tw = this.trigger.getWidth();
18735         tw += this.addicon ? this.addicon.getWidth() : 0;
18736         tw += this.editicon ? this.editicon.getWidth() : 0;
18737         var x = w - tw;
18738         this.el.setWidth( this.adjustWidth('input', x));
18739             
18740         this.trigger.setStyle('left', x+'px');
18741         
18742         if(this.list && this.listWidth === undefined){
18743             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18744             this.list.setWidth(lw);
18745             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18746         }
18747         
18748     
18749         
18750     },
18751
18752     /**
18753      * Allow or prevent the user from directly editing the field text.  If false is passed,
18754      * the user will only be able to select from the items defined in the dropdown list.  This method
18755      * is the runtime equivalent of setting the 'editable' config option at config time.
18756      * @param {Boolean} value True to allow the user to directly edit the field text
18757      */
18758     setEditable : function(value){
18759         if(value == this.editable){
18760             return;
18761         }
18762         this.editable = value;
18763         if(!value){
18764             this.el.dom.setAttribute('readOnly', true);
18765             this.el.on('mousedown', this.onTriggerClick,  this);
18766             this.el.addClass('x-combo-noedit');
18767         }else{
18768             this.el.dom.setAttribute('readOnly', false);
18769             this.el.un('mousedown', this.onTriggerClick,  this);
18770             this.el.removeClass('x-combo-noedit');
18771         }
18772     },
18773
18774     // private
18775     onBeforeLoad : function(){
18776         if(!this.hasFocus){
18777             return;
18778         }
18779         this.innerList.update(this.loadingText ?
18780                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18781         this.restrictHeight();
18782         this.selectedIndex = -1;
18783     },
18784
18785     // private
18786     onLoad : function(){
18787         if(!this.hasFocus){
18788             return;
18789         }
18790         if(this.store.getCount() > 0){
18791             this.expand();
18792             this.restrictHeight();
18793             if(this.lastQuery == this.allQuery){
18794                 if(this.editable){
18795                     this.el.dom.select();
18796                 }
18797                 if(!this.selectByValue(this.value, true)){
18798                     this.select(0, true);
18799                 }
18800             }else{
18801                 this.selectNext();
18802                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18803                     this.taTask.delay(this.typeAheadDelay);
18804                 }
18805             }
18806         }else{
18807             this.onEmptyResults();
18808         }
18809         //this.el.focus();
18810     },
18811     // private
18812     onLoadException : function()
18813     {
18814         this.collapse();
18815         Roo.log(this.store.reader.jsonData);
18816         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18817             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18818         }
18819         
18820         
18821     },
18822     // private
18823     onTypeAhead : function(){
18824         if(this.store.getCount() > 0){
18825             var r = this.store.getAt(0);
18826             var newValue = r.data[this.displayField];
18827             var len = newValue.length;
18828             var selStart = this.getRawValue().length;
18829             if(selStart != len){
18830                 this.setRawValue(newValue);
18831                 this.selectText(selStart, newValue.length);
18832             }
18833         }
18834     },
18835
18836     // private
18837     onSelect : function(record, index){
18838         if(this.fireEvent('beforeselect', this, record, index) !== false){
18839             this.setFromData(index > -1 ? record.data : false);
18840             this.collapse();
18841             this.fireEvent('select', this, record, index);
18842         }
18843     },
18844
18845     /**
18846      * Returns the currently selected field value or empty string if no value is set.
18847      * @return {String} value The selected value
18848      */
18849     getValue : function(){
18850         if(this.valueField){
18851             return typeof this.value != 'undefined' ? this.value : '';
18852         }
18853         return Roo.form.ComboBox.superclass.getValue.call(this);
18854     },
18855
18856     /**
18857      * Clears any text/value currently set in the field
18858      */
18859     clearValue : function(){
18860         if(this.hiddenField){
18861             this.hiddenField.value = '';
18862         }
18863         this.value = '';
18864         this.setRawValue('');
18865         this.lastSelectionText = '';
18866         
18867     },
18868
18869     /**
18870      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18871      * will be displayed in the field.  If the value does not match the data value of an existing item,
18872      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18873      * Otherwise the field will be blank (although the value will still be set).
18874      * @param {String} value The value to match
18875      */
18876     setValue : function(v){
18877         var text = v;
18878         if(this.valueField){
18879             var r = this.findRecord(this.valueField, v);
18880             if(r){
18881                 text = r.data[this.displayField];
18882             }else if(this.valueNotFoundText !== undefined){
18883                 text = this.valueNotFoundText;
18884             }
18885         }
18886         this.lastSelectionText = text;
18887         if(this.hiddenField){
18888             this.hiddenField.value = v;
18889         }
18890         Roo.form.ComboBox.superclass.setValue.call(this, text);
18891         this.value = v;
18892     },
18893     /**
18894      * @property {Object} the last set data for the element
18895      */
18896     
18897     lastData : false,
18898     /**
18899      * Sets the value of the field based on a object which is related to the record format for the store.
18900      * @param {Object} value the value to set as. or false on reset?
18901      */
18902     setFromData : function(o){
18903         var dv = ''; // display value
18904         var vv = ''; // value value..
18905         this.lastData = o;
18906         if (this.displayField) {
18907             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18908         } else {
18909             // this is an error condition!!!
18910             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18911         }
18912         
18913         if(this.valueField){
18914             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18915         }
18916         if(this.hiddenField){
18917             this.hiddenField.value = vv;
18918             
18919             this.lastSelectionText = dv;
18920             Roo.form.ComboBox.superclass.setValue.call(this, dv);
18921             this.value = vv;
18922             return;
18923         }
18924         // no hidden field.. - we store the value in 'value', but still display
18925         // display field!!!!
18926         this.lastSelectionText = dv;
18927         Roo.form.ComboBox.superclass.setValue.call(this, dv);
18928         this.value = vv;
18929         
18930         
18931     },
18932     // private
18933     reset : function(){
18934         // overridden so that last data is reset..
18935         this.setValue(this.resetValue);
18936         this.originalValue = this.getValue();
18937         this.clearInvalid();
18938         this.lastData = false;
18939         if (this.view) {
18940             this.view.clearSelections();
18941         }
18942     },
18943     // private
18944     findRecord : function(prop, value){
18945         var record;
18946         if(this.store.getCount() > 0){
18947             this.store.each(function(r){
18948                 if(r.data[prop] == value){
18949                     record = r;
18950                     return false;
18951                 }
18952                 return true;
18953             });
18954         }
18955         return record;
18956     },
18957     
18958     getName: function()
18959     {
18960         // returns hidden if it's set..
18961         if (!this.rendered) {return ''};
18962         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
18963         
18964     },
18965     // private
18966     onViewMove : function(e, t){
18967         this.inKeyMode = false;
18968     },
18969
18970     // private
18971     onViewOver : function(e, t){
18972         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18973             return;
18974         }
18975         var item = this.view.findItemFromChild(t);
18976         if(item){
18977             var index = this.view.indexOf(item);
18978             this.select(index, false);
18979         }
18980     },
18981
18982     // private
18983     onViewClick : function(doFocus)
18984     {
18985         var index = this.view.getSelectedIndexes()[0];
18986         var r = this.store.getAt(index);
18987         if(r){
18988             this.onSelect(r, index);
18989         }
18990         if(doFocus !== false && !this.blockFocus){
18991             this.el.focus();
18992         }
18993     },
18994
18995     // private
18996     restrictHeight : function(){
18997         this.innerList.dom.style.height = '';
18998         var inner = this.innerList.dom;
18999         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19000         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19001         this.list.beginUpdate();
19002         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19003         this.list.alignTo(this.el, this.listAlign);
19004         this.list.endUpdate();
19005     },
19006
19007     // private
19008     onEmptyResults : function(){
19009         this.collapse();
19010     },
19011
19012     /**
19013      * Returns true if the dropdown list is expanded, else false.
19014      */
19015     isExpanded : function(){
19016         return this.list.isVisible();
19017     },
19018
19019     /**
19020      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19021      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19022      * @param {String} value The data value of the item to select
19023      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19024      * selected item if it is not currently in view (defaults to true)
19025      * @return {Boolean} True if the value matched an item in the list, else false
19026      */
19027     selectByValue : function(v, scrollIntoView){
19028         if(v !== undefined && v !== null){
19029             var r = this.findRecord(this.valueField || this.displayField, v);
19030             if(r){
19031                 this.select(this.store.indexOf(r), scrollIntoView);
19032                 return true;
19033             }
19034         }
19035         return false;
19036     },
19037
19038     /**
19039      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19040      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19041      * @param {Number} index The zero-based index of the list item to select
19042      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19043      * selected item if it is not currently in view (defaults to true)
19044      */
19045     select : function(index, scrollIntoView){
19046         this.selectedIndex = index;
19047         this.view.select(index);
19048         if(scrollIntoView !== false){
19049             var el = this.view.getNode(index);
19050             if(el){
19051                 this.innerList.scrollChildIntoView(el, false);
19052             }
19053         }
19054     },
19055
19056     // private
19057     selectNext : function(){
19058         var ct = this.store.getCount();
19059         if(ct > 0){
19060             if(this.selectedIndex == -1){
19061                 this.select(0);
19062             }else if(this.selectedIndex < ct-1){
19063                 this.select(this.selectedIndex+1);
19064             }
19065         }
19066     },
19067
19068     // private
19069     selectPrev : function(){
19070         var ct = this.store.getCount();
19071         if(ct > 0){
19072             if(this.selectedIndex == -1){
19073                 this.select(0);
19074             }else if(this.selectedIndex != 0){
19075                 this.select(this.selectedIndex-1);
19076             }
19077         }
19078     },
19079
19080     // private
19081     onKeyUp : function(e){
19082         if(this.editable !== false && !e.isSpecialKey()){
19083             this.lastKey = e.getKey();
19084             this.dqTask.delay(this.queryDelay);
19085         }
19086     },
19087
19088     // private
19089     validateBlur : function(){
19090         return !this.list || !this.list.isVisible();   
19091     },
19092
19093     // private
19094     initQuery : function(){
19095         this.doQuery(this.getRawValue());
19096     },
19097
19098     // private
19099     doForce : function(){
19100         if(this.el.dom.value.length > 0){
19101             this.el.dom.value =
19102                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19103              
19104         }
19105     },
19106
19107     /**
19108      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19109      * query allowing the query action to be canceled if needed.
19110      * @param {String} query The SQL query to execute
19111      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19112      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19113      * saved in the current store (defaults to false)
19114      */
19115     doQuery : function(q, forceAll){
19116         if(q === undefined || q === null){
19117             q = '';
19118         }
19119         var qe = {
19120             query: q,
19121             forceAll: forceAll,
19122             combo: this,
19123             cancel:false
19124         };
19125         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19126             return false;
19127         }
19128         q = qe.query;
19129         forceAll = qe.forceAll;
19130         if(forceAll === true || (q.length >= this.minChars)){
19131             if(this.lastQuery != q || this.alwaysQuery){
19132                 this.lastQuery = q;
19133                 if(this.mode == 'local'){
19134                     this.selectedIndex = -1;
19135                     if(forceAll){
19136                         this.store.clearFilter();
19137                     }else{
19138                         this.store.filter(this.displayField, q);
19139                     }
19140                     this.onLoad();
19141                 }else{
19142                     this.store.baseParams[this.queryParam] = q;
19143                     this.store.load({
19144                         params: this.getParams(q)
19145                     });
19146                     this.expand();
19147                 }
19148             }else{
19149                 this.selectedIndex = -1;
19150                 this.onLoad();   
19151             }
19152         }
19153     },
19154
19155     // private
19156     getParams : function(q){
19157         var p = {};
19158         //p[this.queryParam] = q;
19159         if(this.pageSize){
19160             p.start = 0;
19161             p.limit = this.pageSize;
19162         }
19163         return p;
19164     },
19165
19166     /**
19167      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19168      */
19169     collapse : function(){
19170         if(!this.isExpanded()){
19171             return;
19172         }
19173         this.list.hide();
19174         Roo.get(document).un('mousedown', this.collapseIf, this);
19175         Roo.get(document).un('mousewheel', this.collapseIf, this);
19176         if (!this.editable) {
19177             Roo.get(document).un('keydown', this.listKeyPress, this);
19178         }
19179         this.fireEvent('collapse', this);
19180     },
19181
19182     // private
19183     collapseIf : function(e){
19184         if(!e.within(this.wrap) && !e.within(this.list)){
19185             this.collapse();
19186         }
19187     },
19188
19189     /**
19190      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19191      */
19192     expand : function(){
19193         if(this.isExpanded() || !this.hasFocus){
19194             return;
19195         }
19196         this.list.alignTo(this.el, this.listAlign);
19197         this.list.show();
19198         Roo.get(document).on('mousedown', this.collapseIf, this);
19199         Roo.get(document).on('mousewheel', this.collapseIf, this);
19200         if (!this.editable) {
19201             Roo.get(document).on('keydown', this.listKeyPress, this);
19202         }
19203         
19204         this.fireEvent('expand', this);
19205     },
19206
19207     // private
19208     // Implements the default empty TriggerField.onTriggerClick function
19209     onTriggerClick : function(){
19210         if(this.disabled){
19211             return;
19212         }
19213         if(this.isExpanded()){
19214             this.collapse();
19215             if (!this.blockFocus) {
19216                 this.el.focus();
19217             }
19218             
19219         }else {
19220             this.hasFocus = true;
19221             if(this.triggerAction == 'all') {
19222                 this.doQuery(this.allQuery, true);
19223             } else {
19224                 this.doQuery(this.getRawValue());
19225             }
19226             if (!this.blockFocus) {
19227                 this.el.focus();
19228             }
19229         }
19230     },
19231     listKeyPress : function(e)
19232     {
19233         //Roo.log('listkeypress');
19234         // scroll to first matching element based on key pres..
19235         if (e.isSpecialKey()) {
19236             return false;
19237         }
19238         var k = String.fromCharCode(e.getKey()).toUpperCase();
19239         //Roo.log(k);
19240         var match  = false;
19241         var csel = this.view.getSelectedNodes();
19242         var cselitem = false;
19243         if (csel.length) {
19244             var ix = this.view.indexOf(csel[0]);
19245             cselitem  = this.store.getAt(ix);
19246             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19247                 cselitem = false;
19248             }
19249             
19250         }
19251         
19252         this.store.each(function(v) { 
19253             if (cselitem) {
19254                 // start at existing selection.
19255                 if (cselitem.id == v.id) {
19256                     cselitem = false;
19257                 }
19258                 return;
19259             }
19260                 
19261             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19262                 match = this.store.indexOf(v);
19263                 return false;
19264             }
19265         }, this);
19266         
19267         if (match === false) {
19268             return true; // no more action?
19269         }
19270         // scroll to?
19271         this.view.select(match);
19272         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19273         sn.scrollIntoView(sn.dom.parentNode, false);
19274     } 
19275
19276     /** 
19277     * @cfg {Boolean} grow 
19278     * @hide 
19279     */
19280     /** 
19281     * @cfg {Number} growMin 
19282     * @hide 
19283     */
19284     /** 
19285     * @cfg {Number} growMax 
19286     * @hide 
19287     */
19288     /**
19289      * @hide
19290      * @method autoSize
19291      */
19292 });/*
19293  * Copyright(c) 2010-2012, Roo J Solutions Limited
19294  *
19295  * Licence LGPL
19296  *
19297  */
19298
19299 /**
19300  * @class Roo.form.ComboBoxArray
19301  * @extends Roo.form.TextField
19302  * A facebook style adder... for lists of email / people / countries  etc...
19303  * pick multiple items from a combo box, and shows each one.
19304  *
19305  *  Fred [x]  Brian [x]  [Pick another |v]
19306  *
19307  *
19308  *  For this to work: it needs various extra information
19309  *    - normal combo problay has
19310  *      name, hiddenName
19311  *    + displayField, valueField
19312  *
19313  *    For our purpose...
19314  *
19315  *
19316  *   If we change from 'extends' to wrapping...
19317  *   
19318  *  
19319  *
19320  
19321  
19322  * @constructor
19323  * Create a new ComboBoxArray.
19324  * @param {Object} config Configuration options
19325  */
19326  
19327
19328 Roo.form.ComboBoxArray = function(config)
19329 {
19330     this.addEvents({
19331         /**
19332          * @event beforeremove
19333          * Fires before remove the value from the list
19334              * @param {Roo.form.ComboBoxArray} _self This combo box array
19335              * @param {Roo.form.ComboBoxArray.Item} item removed item
19336              */
19337         'beforeremove' : true,
19338         /**
19339          * @event remove
19340          * Fires when remove the value from the list
19341              * @param {Roo.form.ComboBoxArray} _self This combo box array
19342              * @param {Roo.form.ComboBoxArray.Item} item removed item
19343              */
19344         'remove' : true
19345         
19346         
19347     });
19348     
19349     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19350     
19351     this.items = new Roo.util.MixedCollection(false);
19352     
19353     // construct the child combo...
19354     
19355     
19356     
19357     
19358    
19359     
19360 }
19361
19362  
19363 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19364
19365     /**
19366      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19367      */
19368     
19369     lastData : false,
19370     
19371     // behavies liek a hiddne field
19372     inputType:      'hidden',
19373     /**
19374      * @cfg {Number} width The width of the box that displays the selected element
19375      */ 
19376     width:          300,
19377
19378     
19379     
19380     /**
19381      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19382      */
19383     name : false,
19384     /**
19385      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19386      */
19387     hiddenName : false,
19388       /**
19389      * @cfg {String} seperator    The value seperator normally ',' 
19390      */
19391     seperator : ',',
19392     
19393     // private the array of items that are displayed..
19394     items  : false,
19395     // private - the hidden field el.
19396     hiddenEl : false,
19397     // private - the filed el..
19398     el : false,
19399     
19400     //validateValue : function() { return true; }, // all values are ok!
19401     //onAddClick: function() { },
19402     
19403     onRender : function(ct, position) 
19404     {
19405         
19406         // create the standard hidden element
19407         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19408         
19409         
19410         // give fake names to child combo;
19411         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19412         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19413         
19414         this.combo = Roo.factory(this.combo, Roo.form);
19415         this.combo.onRender(ct, position);
19416         if (typeof(this.combo.width) != 'undefined') {
19417             this.combo.onResize(this.combo.width,0);
19418         }
19419         
19420         this.combo.initEvents();
19421         
19422         // assigned so form know we need to do this..
19423         this.store          = this.combo.store;
19424         this.valueField     = this.combo.valueField;
19425         this.displayField   = this.combo.displayField ;
19426         
19427         
19428         this.combo.wrap.addClass('x-cbarray-grp');
19429         
19430         var cbwrap = this.combo.wrap.createChild(
19431             {tag: 'div', cls: 'x-cbarray-cb'},
19432             this.combo.el.dom
19433         );
19434         
19435              
19436         this.hiddenEl = this.combo.wrap.createChild({
19437             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19438         });
19439         this.el = this.combo.wrap.createChild({
19440             tag: 'input',  type:'hidden' , name: this.name, value : ''
19441         });
19442          //   this.el.dom.removeAttribute("name");
19443         
19444         
19445         this.outerWrap = this.combo.wrap;
19446         this.wrap = cbwrap;
19447         
19448         this.outerWrap.setWidth(this.width);
19449         this.outerWrap.dom.removeChild(this.el.dom);
19450         
19451         this.wrap.dom.appendChild(this.el.dom);
19452         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19453         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19454         
19455         this.combo.trigger.setStyle('position','relative');
19456         this.combo.trigger.setStyle('left', '0px');
19457         this.combo.trigger.setStyle('top', '2px');
19458         
19459         this.combo.el.setStyle('vertical-align', 'text-bottom');
19460         
19461         //this.trigger.setStyle('vertical-align', 'top');
19462         
19463         // this should use the code from combo really... on('add' ....)
19464         if (this.adder) {
19465             
19466         
19467             this.adder = this.outerWrap.createChild(
19468                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19469             var _t = this;
19470             this.adder.on('click', function(e) {
19471                 _t.fireEvent('adderclick', this, e);
19472             }, _t);
19473         }
19474         //var _t = this;
19475         //this.adder.on('click', this.onAddClick, _t);
19476         
19477         
19478         this.combo.on('select', function(cb, rec, ix) {
19479             this.addItem(rec.data);
19480             
19481             cb.setValue('');
19482             cb.el.dom.value = '';
19483             //cb.lastData = rec.data;
19484             // add to list
19485             
19486         }, this);
19487         
19488         
19489     },
19490     
19491     
19492     getName: function()
19493     {
19494         // returns hidden if it's set..
19495         if (!this.rendered) {return ''};
19496         return  this.hiddenName ? this.hiddenName : this.name;
19497         
19498     },
19499     
19500     
19501     onResize: function(w, h){
19502         
19503         return;
19504         // not sure if this is needed..
19505         //this.combo.onResize(w,h);
19506         
19507         if(typeof w != 'number'){
19508             // we do not handle it!?!?
19509             return;
19510         }
19511         var tw = this.combo.trigger.getWidth();
19512         tw += this.addicon ? this.addicon.getWidth() : 0;
19513         tw += this.editicon ? this.editicon.getWidth() : 0;
19514         var x = w - tw;
19515         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19516             
19517         this.combo.trigger.setStyle('left', '0px');
19518         
19519         if(this.list && this.listWidth === undefined){
19520             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19521             this.list.setWidth(lw);
19522             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19523         }
19524         
19525     
19526         
19527     },
19528     
19529     addItem: function(rec)
19530     {
19531         var valueField = this.combo.valueField;
19532         var displayField = this.combo.displayField;
19533         
19534         if (this.items.indexOfKey(rec[valueField]) > -1) {
19535             //console.log("GOT " + rec.data.id);
19536             return;
19537         }
19538         
19539         var x = new Roo.form.ComboBoxArray.Item({
19540             //id : rec[this.idField],
19541             data : rec,
19542             displayField : displayField ,
19543             tipField : displayField ,
19544             cb : this
19545         });
19546         // use the 
19547         this.items.add(rec[valueField],x);
19548         // add it before the element..
19549         this.updateHiddenEl();
19550         x.render(this.outerWrap, this.wrap.dom);
19551         // add the image handler..
19552     },
19553     
19554     updateHiddenEl : function()
19555     {
19556         this.validate();
19557         if (!this.hiddenEl) {
19558             return;
19559         }
19560         var ar = [];
19561         var idField = this.combo.valueField;
19562         
19563         this.items.each(function(f) {
19564             ar.push(f.data[idField]);
19565         });
19566         this.hiddenEl.dom.value = ar.join(this.seperator);
19567         this.validate();
19568     },
19569     
19570     reset : function()
19571     {
19572         this.items.clear();
19573         
19574         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19575            el.remove();
19576         });
19577         
19578         this.el.dom.value = '';
19579         if (this.hiddenEl) {
19580             this.hiddenEl.dom.value = '';
19581         }
19582         
19583     },
19584     getValue: function()
19585     {
19586         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19587     },
19588     setValue: function(v) // not a valid action - must use addItems..
19589     {
19590         
19591         this.reset();
19592          
19593         if (this.store.isLocal && (typeof(v) == 'string')) {
19594             // then we can use the store to find the values..
19595             // comma seperated at present.. this needs to allow JSON based encoding..
19596             this.hiddenEl.value  = v;
19597             var v_ar = [];
19598             Roo.each(v.split(this.seperator), function(k) {
19599                 Roo.log("CHECK " + this.valueField + ',' + k);
19600                 var li = this.store.query(this.valueField, k);
19601                 if (!li.length) {
19602                     return;
19603                 }
19604                 var add = {};
19605                 add[this.valueField] = k;
19606                 add[this.displayField] = li.item(0).data[this.displayField];
19607                 
19608                 this.addItem(add);
19609             }, this) 
19610              
19611         }
19612         if (typeof(v) == 'object' ) {
19613             // then let's assume it's an array of objects..
19614             Roo.each(v, function(l) {
19615                 var add = l;
19616                 if (typeof(l) == 'string') {
19617                     add = {};
19618                     add[this.valueField] = l;
19619                     add[this.displayField] = l
19620                 }
19621                 this.addItem(add);
19622             }, this);
19623              
19624         }
19625         
19626         
19627     },
19628     setFromData: function(v)
19629     {
19630         // this recieves an object, if setValues is called.
19631         this.reset();
19632         this.el.dom.value = v[this.displayField];
19633         this.hiddenEl.dom.value = v[this.valueField];
19634         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19635             return;
19636         }
19637         var kv = v[this.valueField];
19638         var dv = v[this.displayField];
19639         kv = typeof(kv) != 'string' ? '' : kv;
19640         dv = typeof(dv) != 'string' ? '' : dv;
19641         
19642         
19643         var keys = kv.split(this.seperator);
19644         var display = dv.split(this.seperator);
19645         for (var i = 0 ; i < keys.length; i++) {
19646             add = {};
19647             add[this.valueField] = keys[i];
19648             add[this.displayField] = display[i];
19649             this.addItem(add);
19650         }
19651       
19652         
19653     },
19654     
19655     /**
19656      * Validates the combox array value
19657      * @return {Boolean} True if the value is valid, else false
19658      */
19659     validate : function(){
19660         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19661             this.clearInvalid();
19662             return true;
19663         }
19664         return false;
19665     },
19666     
19667     validateValue : function(value){
19668         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19669         
19670     },
19671     
19672     /*@
19673      * overide
19674      * 
19675      */
19676     isDirty : function() {
19677         if(this.disabled) {
19678             return false;
19679         }
19680         
19681         try {
19682             var d = Roo.decode(String(this.originalValue));
19683         } catch (e) {
19684             return String(this.getValue()) !== String(this.originalValue);
19685         }
19686         
19687         var originalValue = [];
19688         
19689         for (var i = 0; i < d.length; i++){
19690             originalValue.push(d[i][this.valueField]);
19691         }
19692         
19693         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19694         
19695     }
19696     
19697 });
19698
19699
19700
19701 /**
19702  * @class Roo.form.ComboBoxArray.Item
19703  * @extends Roo.BoxComponent
19704  * A selected item in the list
19705  *  Fred [x]  Brian [x]  [Pick another |v]
19706  * 
19707  * @constructor
19708  * Create a new item.
19709  * @param {Object} config Configuration options
19710  */
19711  
19712 Roo.form.ComboBoxArray.Item = function(config) {
19713     config.id = Roo.id();
19714     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19715 }
19716
19717 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19718     data : {},
19719     cb: false,
19720     displayField : false,
19721     tipField : false,
19722     
19723     
19724     defaultAutoCreate : {
19725         tag: 'div',
19726         cls: 'x-cbarray-item',
19727         cn : [ 
19728             { tag: 'div' },
19729             {
19730                 tag: 'img',
19731                 width:16,
19732                 height : 16,
19733                 src : Roo.BLANK_IMAGE_URL ,
19734                 align: 'center'
19735             }
19736         ]
19737         
19738     },
19739     
19740  
19741     onRender : function(ct, position)
19742     {
19743         Roo.form.Field.superclass.onRender.call(this, ct, position);
19744         
19745         if(!this.el){
19746             var cfg = this.getAutoCreate();
19747             this.el = ct.createChild(cfg, position);
19748         }
19749         
19750         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19751         
19752         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19753             this.cb.renderer(this.data) :
19754             String.format('{0}',this.data[this.displayField]);
19755         
19756             
19757         this.el.child('div').dom.setAttribute('qtip',
19758                         String.format('{0}',this.data[this.tipField])
19759         );
19760         
19761         this.el.child('img').on('click', this.remove, this);
19762         
19763     },
19764    
19765     remove : function()
19766     {
19767         if(this.cb.disabled){
19768             return;
19769         }
19770         
19771         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19772             this.cb.items.remove(this);
19773             this.el.child('img').un('click', this.remove, this);
19774             this.el.remove();
19775             this.cb.updateHiddenEl();
19776
19777             this.cb.fireEvent('remove', this.cb, this);
19778         }
19779         
19780     }
19781 });/*
19782  * RooJS Library 1.1.1
19783  * Copyright(c) 2008-2011  Alan Knowles
19784  *
19785  * License - LGPL
19786  */
19787  
19788
19789 /**
19790  * @class Roo.form.ComboNested
19791  * @extends Roo.form.ComboBox
19792  * A combobox for that allows selection of nested items in a list,
19793  * eg.
19794  *
19795  *  Book
19796  *    -> red
19797  *    -> green
19798  *  Table
19799  *    -> square
19800  *      ->red
19801  *      ->green
19802  *    -> rectangle
19803  *      ->green
19804  *      
19805  * 
19806  * @constructor
19807  * Create a new ComboNested
19808  * @param {Object} config Configuration options
19809  */
19810 Roo.form.ComboNested = function(config){
19811     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19812     // should verify some data...
19813     // like
19814     // hiddenName = required..
19815     // displayField = required
19816     // valudField == required
19817     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19818     var _t = this;
19819     Roo.each(req, function(e) {
19820         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19821             throw "Roo.form.ComboNested : missing value for: " + e;
19822         }
19823     });
19824      
19825     
19826 };
19827
19828 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19829    
19830     /*
19831      * @config {Number} max Number of columns to show
19832      */
19833     
19834     maxColumns : 3,
19835    
19836     list : null, // the outermost div..
19837     innerLists : null, // the
19838     views : null,
19839     stores : null,
19840     // private
19841     loadingChildren : false,
19842     
19843     onRender : function(ct, position)
19844     {
19845         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19846         
19847         if(this.hiddenName){
19848             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19849                     'before', true);
19850             this.hiddenField.value =
19851                 this.hiddenValue !== undefined ? this.hiddenValue :
19852                 this.value !== undefined ? this.value : '';
19853
19854             // prevent input submission
19855             this.el.dom.removeAttribute('name');
19856              
19857              
19858         }
19859         
19860         if(Roo.isGecko){
19861             this.el.dom.setAttribute('autocomplete', 'off');
19862         }
19863
19864         var cls = 'x-combo-list';
19865
19866         this.list = new Roo.Layer({
19867             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19868         });
19869
19870         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19871         this.list.setWidth(lw);
19872         this.list.swallowEvent('mousewheel');
19873         this.assetHeight = 0;
19874
19875         if(this.title){
19876             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19877             this.assetHeight += this.header.getHeight();
19878         }
19879         this.innerLists = [];
19880         this.views = [];
19881         this.stores = [];
19882         for (var i =0 ; i < this.maxColumns; i++) {
19883             this.onRenderList( cls, i);
19884         }
19885         
19886         // always needs footer, as we are going to have an 'OK' button.
19887         this.footer = this.list.createChild({cls:cls+'-ft'});
19888         this.pageTb = new Roo.Toolbar(this.footer);  
19889         var _this = this;
19890         this.pageTb.add(  {
19891             
19892             text: 'Done',
19893             handler: function()
19894             {
19895                 _this.collapse();
19896             }
19897         });
19898         
19899         if ( this.allowBlank && !this.disableClear) {
19900             
19901             this.pageTb.add(new Roo.Toolbar.Fill(), {
19902                 cls: 'x-btn-icon x-btn-clear',
19903                 text: '&#160;',
19904                 handler: function()
19905                 {
19906                     _this.collapse();
19907                     _this.clearValue();
19908                     _this.onSelect(false, -1);
19909                 }
19910             });
19911         }
19912         if (this.footer) {
19913             this.assetHeight += this.footer.getHeight();
19914         }
19915         
19916     },
19917     onRenderList : function (  cls, i)
19918     {
19919         
19920         var lw = Math.floor(
19921                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
19922         );
19923         
19924         this.list.setWidth(lw); // default to '1'
19925
19926         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
19927         //il.on('mouseover', this.onViewOver, this, { list:  i });
19928         //il.on('mousemove', this.onViewMove, this, { list:  i });
19929         il.setWidth(lw);
19930         il.setStyle({ 'overflow-x' : 'hidden'});
19931
19932         if(!this.tpl){
19933             this.tpl = new Roo.Template({
19934                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
19935                 isEmpty: function (value, allValues) {
19936                     //Roo.log(value);
19937                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
19938                     return dl ? 'has-children' : 'no-children'
19939                 }
19940             });
19941         }
19942         
19943         var store  = this.store;
19944         if (i > 0) {
19945             store  = new Roo.data.SimpleStore({
19946                 //fields : this.store.reader.meta.fields,
19947                 reader : this.store.reader,
19948                 data : [ ]
19949             });
19950         }
19951         this.stores[i]  = store;
19952                   
19953         var view = this.views[i] = new Roo.View(
19954             il,
19955             this.tpl,
19956             {
19957                 singleSelect:true,
19958                 store: store,
19959                 selectedClass: this.selectedClass
19960             }
19961         );
19962         view.getEl().setWidth(lw);
19963         view.getEl().setStyle({
19964             position: i < 1 ? 'relative' : 'absolute',
19965             top: 0,
19966             left: (i * lw ) + 'px',
19967             display : i > 0 ? 'none' : 'block'
19968         });
19969         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
19970         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
19971         //view.on('click', this.onViewClick, this, { list : i });
19972
19973         store.on('beforeload', this.onBeforeLoad, this);
19974         store.on('load',  this.onLoad, this, { list  : i});
19975         store.on('loadexception', this.onLoadException, this);
19976
19977         // hide the other vies..
19978         
19979         
19980         
19981     },
19982       
19983     restrictHeight : function()
19984     {
19985         var mh = 0;
19986         Roo.each(this.innerLists, function(il,i) {
19987             var el = this.views[i].getEl();
19988             el.dom.style.height = '';
19989             var inner = el.dom;
19990             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
19991             // only adjust heights on other ones..
19992             mh = Math.max(h, mh);
19993             if (i < 1) {
19994                 
19995                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19996                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19997                
19998             }
19999             
20000             
20001         }, this);
20002         
20003         this.list.beginUpdate();
20004         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20005         this.list.alignTo(this.el, this.listAlign);
20006         this.list.endUpdate();
20007         
20008     },
20009      
20010     
20011     // -- store handlers..
20012     // private
20013     onBeforeLoad : function()
20014     {
20015         if(!this.hasFocus){
20016             return;
20017         }
20018         this.innerLists[0].update(this.loadingText ?
20019                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20020         this.restrictHeight();
20021         this.selectedIndex = -1;
20022     },
20023     // private
20024     onLoad : function(a,b,c,d)
20025     {
20026         if (!this.loadingChildren) {
20027             // then we are loading the top level. - hide the children
20028             for (var i = 1;i < this.views.length; i++) {
20029                 this.views[i].getEl().setStyle({ display : 'none' });
20030             }
20031             var lw = Math.floor(
20032                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20033             );
20034         
20035              this.list.setWidth(lw); // default to '1'
20036
20037             
20038         }
20039         if(!this.hasFocus){
20040             return;
20041         }
20042         
20043         if(this.store.getCount() > 0) {
20044             this.expand();
20045             this.restrictHeight();   
20046         } else {
20047             this.onEmptyResults();
20048         }
20049         
20050         if (!this.loadingChildren) {
20051             this.selectActive();
20052         }
20053         /*
20054         this.stores[1].loadData([]);
20055         this.stores[2].loadData([]);
20056         this.views
20057         */    
20058     
20059         //this.el.focus();
20060     },
20061     
20062     
20063     // private
20064     onLoadException : function()
20065     {
20066         this.collapse();
20067         Roo.log(this.store.reader.jsonData);
20068         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20069             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20070         }
20071         
20072         
20073     },
20074     // no cleaning of leading spaces on blur here.
20075     cleanLeadingSpace : function(e) { },
20076     
20077
20078     onSelectChange : function (view, sels, opts )
20079     {
20080         var ix = view.getSelectedIndexes();
20081          
20082         if (opts.list > this.maxColumns - 2) {
20083             if (view.store.getCount()<  1) {
20084                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20085
20086             } else  {
20087                 if (ix.length) {
20088                     // used to clear ?? but if we are loading unselected 
20089                     this.setFromData(view.store.getAt(ix[0]).data);
20090                 }
20091                 
20092             }
20093             
20094             return;
20095         }
20096         
20097         if (!ix.length) {
20098             // this get's fired when trigger opens..
20099            // this.setFromData({});
20100             var str = this.stores[opts.list+1];
20101             str.data.clear(); // removeall wihtout the fire events..
20102             return;
20103         }
20104         
20105         var rec = view.store.getAt(ix[0]);
20106          
20107         this.setFromData(rec.data);
20108         this.fireEvent('select', this, rec, ix[0]);
20109         
20110         var lw = Math.floor(
20111              (
20112                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20113              ) / this.maxColumns
20114         );
20115         this.loadingChildren = true;
20116         this.stores[opts.list+1].loadDataFromChildren( rec );
20117         this.loadingChildren = false;
20118         var dl = this.stores[opts.list+1]. getTotalCount();
20119         
20120         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20121         
20122         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20123         for (var i = opts.list+2; i < this.views.length;i++) {
20124             this.views[i].getEl().setStyle({ display : 'none' });
20125         }
20126         
20127         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20128         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20129         
20130         if (this.isLoading) {
20131            // this.selectActive(opts.list);
20132         }
20133          
20134     },
20135     
20136     
20137     
20138     
20139     onDoubleClick : function()
20140     {
20141         this.collapse(); //??
20142     },
20143     
20144      
20145     
20146     
20147     
20148     // private
20149     recordToStack : function(store, prop, value, stack)
20150     {
20151         var cstore = new Roo.data.SimpleStore({
20152             //fields : this.store.reader.meta.fields, // we need array reader.. for
20153             reader : this.store.reader,
20154             data : [ ]
20155         });
20156         var _this = this;
20157         var record  = false;
20158         var srec = false;
20159         if(store.getCount() < 1){
20160             return false;
20161         }
20162         store.each(function(r){
20163             if(r.data[prop] == value){
20164                 record = r;
20165             srec = r;
20166                 return false;
20167             }
20168             if (r.data.cn && r.data.cn.length) {
20169                 cstore.loadDataFromChildren( r);
20170                 var cret = _this.recordToStack(cstore, prop, value, stack);
20171                 if (cret !== false) {
20172                     record = cret;
20173                     srec = r;
20174                     return false;
20175                 }
20176             }
20177              
20178             return true;
20179         });
20180         if (record == false) {
20181             return false
20182         }
20183         stack.unshift(srec);
20184         return record;
20185     },
20186     
20187     /*
20188      * find the stack of stores that match our value.
20189      *
20190      * 
20191      */
20192     
20193     selectActive : function ()
20194     {
20195         // if store is not loaded, then we will need to wait for that to happen first.
20196         var stack = [];
20197         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20198         for (var i = 0; i < stack.length; i++ ) {
20199             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20200         }
20201         
20202     }
20203         
20204          
20205     
20206     
20207     
20208     
20209 });/*
20210  * Based on:
20211  * Ext JS Library 1.1.1
20212  * Copyright(c) 2006-2007, Ext JS, LLC.
20213  *
20214  * Originally Released Under LGPL - original licence link has changed is not relivant.
20215  *
20216  * Fork - LGPL
20217  * <script type="text/javascript">
20218  */
20219 /**
20220  * @class Roo.form.Checkbox
20221  * @extends Roo.form.Field
20222  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20223  * @constructor
20224  * Creates a new Checkbox
20225  * @param {Object} config Configuration options
20226  */
20227 Roo.form.Checkbox = function(config){
20228     Roo.form.Checkbox.superclass.constructor.call(this, config);
20229     this.addEvents({
20230         /**
20231          * @event check
20232          * Fires when the checkbox is checked or unchecked.
20233              * @param {Roo.form.Checkbox} this This checkbox
20234              * @param {Boolean} checked The new checked value
20235              */
20236         check : true
20237     });
20238 };
20239
20240 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20241     /**
20242      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20243      */
20244     focusClass : undefined,
20245     /**
20246      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20247      */
20248     fieldClass: "x-form-field",
20249     /**
20250      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20251      */
20252     checked: false,
20253     /**
20254      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20255      * {tag: "input", type: "checkbox", autocomplete: "off"})
20256      */
20257     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20258     /**
20259      * @cfg {String} boxLabel The text that appears beside the checkbox
20260      */
20261     boxLabel : "",
20262     /**
20263      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20264      */  
20265     inputValue : '1',
20266     /**
20267      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20268      */
20269      valueOff: '0', // value when not checked..
20270
20271     actionMode : 'viewEl', 
20272     //
20273     // private
20274     itemCls : 'x-menu-check-item x-form-item',
20275     groupClass : 'x-menu-group-item',
20276     inputType : 'hidden',
20277     
20278     
20279     inSetChecked: false, // check that we are not calling self...
20280     
20281     inputElement: false, // real input element?
20282     basedOn: false, // ????
20283     
20284     isFormField: true, // not sure where this is needed!!!!
20285
20286     onResize : function(){
20287         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20288         if(!this.boxLabel){
20289             this.el.alignTo(this.wrap, 'c-c');
20290         }
20291     },
20292
20293     initEvents : function(){
20294         Roo.form.Checkbox.superclass.initEvents.call(this);
20295         this.el.on("click", this.onClick,  this);
20296         this.el.on("change", this.onClick,  this);
20297     },
20298
20299
20300     getResizeEl : function(){
20301         return this.wrap;
20302     },
20303
20304     getPositionEl : function(){
20305         return this.wrap;
20306     },
20307
20308     // private
20309     onRender : function(ct, position){
20310         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20311         /*
20312         if(this.inputValue !== undefined){
20313             this.el.dom.value = this.inputValue;
20314         }
20315         */
20316         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20317         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20318         var viewEl = this.wrap.createChild({ 
20319             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20320         this.viewEl = viewEl;   
20321         this.wrap.on('click', this.onClick,  this); 
20322         
20323         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20324         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20325         
20326         
20327         
20328         if(this.boxLabel){
20329             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20330         //    viewEl.on('click', this.onClick,  this); 
20331         }
20332         //if(this.checked){
20333             this.setChecked(this.checked);
20334         //}else{
20335             //this.checked = this.el.dom;
20336         //}
20337
20338     },
20339
20340     // private
20341     initValue : Roo.emptyFn,
20342
20343     /**
20344      * Returns the checked state of the checkbox.
20345      * @return {Boolean} True if checked, else false
20346      */
20347     getValue : function(){
20348         if(this.el){
20349             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20350         }
20351         return this.valueOff;
20352         
20353     },
20354
20355         // private
20356     onClick : function(){ 
20357         if (this.disabled) {
20358             return;
20359         }
20360         this.setChecked(!this.checked);
20361
20362         //if(this.el.dom.checked != this.checked){
20363         //    this.setValue(this.el.dom.checked);
20364        // }
20365     },
20366
20367     /**
20368      * Sets the checked state of the checkbox.
20369      * On is always based on a string comparison between inputValue and the param.
20370      * @param {Boolean/String} value - the value to set 
20371      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20372      */
20373     setValue : function(v,suppressEvent){
20374         
20375         
20376         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20377         //if(this.el && this.el.dom){
20378         //    this.el.dom.checked = this.checked;
20379         //    this.el.dom.defaultChecked = this.checked;
20380         //}
20381         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20382         //this.fireEvent("check", this, this.checked);
20383     },
20384     // private..
20385     setChecked : function(state,suppressEvent)
20386     {
20387         if (this.inSetChecked) {
20388             this.checked = state;
20389             return;
20390         }
20391         
20392     
20393         if(this.wrap){
20394             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20395         }
20396         this.checked = state;
20397         if(suppressEvent !== true){
20398             this.fireEvent('check', this, state);
20399         }
20400         this.inSetChecked = true;
20401         this.el.dom.value = state ? this.inputValue : this.valueOff;
20402         this.inSetChecked = false;
20403         
20404     },
20405     // handle setting of hidden value by some other method!!?!?
20406     setFromHidden: function()
20407     {
20408         if(!this.el){
20409             return;
20410         }
20411         //console.log("SET FROM HIDDEN");
20412         //alert('setFrom hidden');
20413         this.setValue(this.el.dom.value);
20414     },
20415     
20416     onDestroy : function()
20417     {
20418         if(this.viewEl){
20419             Roo.get(this.viewEl).remove();
20420         }
20421          
20422         Roo.form.Checkbox.superclass.onDestroy.call(this);
20423     },
20424     
20425     setBoxLabel : function(str)
20426     {
20427         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20428     }
20429
20430 });/*
20431  * Based on:
20432  * Ext JS Library 1.1.1
20433  * Copyright(c) 2006-2007, Ext JS, LLC.
20434  *
20435  * Originally Released Under LGPL - original licence link has changed is not relivant.
20436  *
20437  * Fork - LGPL
20438  * <script type="text/javascript">
20439  */
20440  
20441 /**
20442  * @class Roo.form.Radio
20443  * @extends Roo.form.Checkbox
20444  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20445  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20446  * @constructor
20447  * Creates a new Radio
20448  * @param {Object} config Configuration options
20449  */
20450 Roo.form.Radio = function(){
20451     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20452 };
20453 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20454     inputType: 'radio',
20455
20456     /**
20457      * If this radio is part of a group, it will return the selected value
20458      * @return {String}
20459      */
20460     getGroupValue : function(){
20461         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20462     },
20463     
20464     
20465     onRender : function(ct, position){
20466         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20467         
20468         if(this.inputValue !== undefined){
20469             this.el.dom.value = this.inputValue;
20470         }
20471          
20472         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20473         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20474         //var viewEl = this.wrap.createChild({ 
20475         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20476         //this.viewEl = viewEl;   
20477         //this.wrap.on('click', this.onClick,  this); 
20478         
20479         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20480         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20481         
20482         
20483         
20484         if(this.boxLabel){
20485             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20486         //    viewEl.on('click', this.onClick,  this); 
20487         }
20488          if(this.checked){
20489             this.el.dom.checked =   'checked' ;
20490         }
20491          
20492     } 
20493     
20494     
20495 });//<script type="text/javascript">
20496
20497 /*
20498  * Based  Ext JS Library 1.1.1
20499  * Copyright(c) 2006-2007, Ext JS, LLC.
20500  * LGPL
20501  *
20502  */
20503  
20504 /**
20505  * @class Roo.HtmlEditorCore
20506  * @extends Roo.Component
20507  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20508  *
20509  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20510  */
20511
20512 Roo.HtmlEditorCore = function(config){
20513     
20514     
20515     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20516     
20517     
20518     this.addEvents({
20519         /**
20520          * @event initialize
20521          * Fires when the editor is fully initialized (including the iframe)
20522          * @param {Roo.HtmlEditorCore} this
20523          */
20524         initialize: true,
20525         /**
20526          * @event activate
20527          * Fires when the editor is first receives the focus. Any insertion must wait
20528          * until after this event.
20529          * @param {Roo.HtmlEditorCore} this
20530          */
20531         activate: true,
20532          /**
20533          * @event beforesync
20534          * Fires before the textarea is updated with content from the editor iframe. Return false
20535          * to cancel the sync.
20536          * @param {Roo.HtmlEditorCore} this
20537          * @param {String} html
20538          */
20539         beforesync: true,
20540          /**
20541          * @event beforepush
20542          * Fires before the iframe editor is updated with content from the textarea. Return false
20543          * to cancel the push.
20544          * @param {Roo.HtmlEditorCore} this
20545          * @param {String} html
20546          */
20547         beforepush: true,
20548          /**
20549          * @event sync
20550          * Fires when the textarea is updated with content from the editor iframe.
20551          * @param {Roo.HtmlEditorCore} this
20552          * @param {String} html
20553          */
20554         sync: true,
20555          /**
20556          * @event push
20557          * Fires when the iframe editor is updated with content from the textarea.
20558          * @param {Roo.HtmlEditorCore} this
20559          * @param {String} html
20560          */
20561         push: true,
20562         
20563         /**
20564          * @event editorevent
20565          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20566          * @param {Roo.HtmlEditorCore} this
20567          */
20568         editorevent: true
20569         
20570     });
20571     
20572     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20573     
20574     // defaults : white / black...
20575     this.applyBlacklists();
20576     
20577     
20578     
20579 };
20580
20581
20582 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20583
20584
20585      /**
20586      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20587      */
20588     
20589     owner : false,
20590     
20591      /**
20592      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20593      *                        Roo.resizable.
20594      */
20595     resizable : false,
20596      /**
20597      * @cfg {Number} height (in pixels)
20598      */   
20599     height: 300,
20600    /**
20601      * @cfg {Number} width (in pixels)
20602      */   
20603     width: 500,
20604     
20605     /**
20606      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20607      * 
20608      */
20609     stylesheets: false,
20610     
20611     // id of frame..
20612     frameId: false,
20613     
20614     // private properties
20615     validationEvent : false,
20616     deferHeight: true,
20617     initialized : false,
20618     activated : false,
20619     sourceEditMode : false,
20620     onFocus : Roo.emptyFn,
20621     iframePad:3,
20622     hideMode:'offsets',
20623     
20624     clearUp: true,
20625     
20626     // blacklist + whitelisted elements..
20627     black: false,
20628     white: false,
20629      
20630     bodyCls : '',
20631
20632     /**
20633      * Protected method that will not generally be called directly. It
20634      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20635      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20636      */
20637     getDocMarkup : function(){
20638         // body styles..
20639         var st = '';
20640         
20641         // inherit styels from page...?? 
20642         if (this.stylesheets === false) {
20643             
20644             Roo.get(document.head).select('style').each(function(node) {
20645                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20646             });
20647             
20648             Roo.get(document.head).select('link').each(function(node) { 
20649                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20650             });
20651             
20652         } else if (!this.stylesheets.length) {
20653                 // simple..
20654                 st = '<style type="text/css">' +
20655                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20656                    '</style>';
20657         } else {
20658             for (var i in this.stylesheets) { 
20659                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
20660             }
20661             
20662         }
20663         
20664         st +=  '<style type="text/css">' +
20665             'IMG { cursor: pointer } ' +
20666         '</style>';
20667
20668         var cls = 'roo-htmleditor-body';
20669         
20670         if(this.bodyCls.length){
20671             cls += ' ' + this.bodyCls;
20672         }
20673         
20674         return '<html><head>' + st  +
20675             //<style type="text/css">' +
20676             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20677             //'</style>' +
20678             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
20679     },
20680
20681     // private
20682     onRender : function(ct, position)
20683     {
20684         var _t = this;
20685         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20686         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20687         
20688         
20689         this.el.dom.style.border = '0 none';
20690         this.el.dom.setAttribute('tabIndex', -1);
20691         this.el.addClass('x-hidden hide');
20692         
20693         
20694         
20695         if(Roo.isIE){ // fix IE 1px bogus margin
20696             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20697         }
20698        
20699         
20700         this.frameId = Roo.id();
20701         
20702          
20703         
20704         var iframe = this.owner.wrap.createChild({
20705             tag: 'iframe',
20706             cls: 'form-control', // bootstrap..
20707             id: this.frameId,
20708             name: this.frameId,
20709             frameBorder : 'no',
20710             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20711         }, this.el
20712         );
20713         
20714         
20715         this.iframe = iframe.dom;
20716
20717          this.assignDocWin();
20718         
20719         this.doc.designMode = 'on';
20720        
20721         this.doc.open();
20722         this.doc.write(this.getDocMarkup());
20723         this.doc.close();
20724
20725         
20726         var task = { // must defer to wait for browser to be ready
20727             run : function(){
20728                 //console.log("run task?" + this.doc.readyState);
20729                 this.assignDocWin();
20730                 if(this.doc.body || this.doc.readyState == 'complete'){
20731                     try {
20732                         this.doc.designMode="on";
20733                     } catch (e) {
20734                         return;
20735                     }
20736                     Roo.TaskMgr.stop(task);
20737                     this.initEditor.defer(10, this);
20738                 }
20739             },
20740             interval : 10,
20741             duration: 10000,
20742             scope: this
20743         };
20744         Roo.TaskMgr.start(task);
20745
20746     },
20747
20748     // private
20749     onResize : function(w, h)
20750     {
20751          Roo.log('resize: ' +w + ',' + h );
20752         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20753         if(!this.iframe){
20754             return;
20755         }
20756         if(typeof w == 'number'){
20757             
20758             this.iframe.style.width = w + 'px';
20759         }
20760         if(typeof h == 'number'){
20761             
20762             this.iframe.style.height = h + 'px';
20763             if(this.doc){
20764                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20765             }
20766         }
20767         
20768     },
20769
20770     /**
20771      * Toggles the editor between standard and source edit mode.
20772      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20773      */
20774     toggleSourceEdit : function(sourceEditMode){
20775         
20776         this.sourceEditMode = sourceEditMode === true;
20777         
20778         if(this.sourceEditMode){
20779  
20780             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20781             
20782         }else{
20783             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20784             //this.iframe.className = '';
20785             this.deferFocus();
20786         }
20787         //this.setSize(this.owner.wrap.getSize());
20788         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20789     },
20790
20791     
20792   
20793
20794     /**
20795      * Protected method that will not generally be called directly. If you need/want
20796      * custom HTML cleanup, this is the method you should override.
20797      * @param {String} html The HTML to be cleaned
20798      * return {String} The cleaned HTML
20799      */
20800     cleanHtml : function(html){
20801         html = String(html);
20802         if(html.length > 5){
20803             if(Roo.isSafari){ // strip safari nonsense
20804                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20805             }
20806         }
20807         if(html == '&nbsp;'){
20808             html = '';
20809         }
20810         return html;
20811     },
20812
20813     /**
20814      * HTML Editor -> Textarea
20815      * Protected method that will not generally be called directly. Syncs the contents
20816      * of the editor iframe with the textarea.
20817      */
20818     syncValue : function(){
20819         if(this.initialized){
20820             var bd = (this.doc.body || this.doc.documentElement);
20821             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20822             var html = bd.innerHTML;
20823             if(Roo.isSafari){
20824                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20825                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20826                 if(m && m[1]){
20827                     html = '<div style="'+m[0]+'">' + html + '</div>';
20828                 }
20829             }
20830             html = this.cleanHtml(html);
20831             // fix up the special chars.. normaly like back quotes in word...
20832             // however we do not want to do this with chinese..
20833             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
20834                 
20835                 var cc = match.charCodeAt();
20836
20837                 // Get the character value, handling surrogate pairs
20838                 if (match.length == 2) {
20839                     // It's a surrogate pair, calculate the Unicode code point
20840                     var high = match.charCodeAt(0) - 0xD800;
20841                     var low  = match.charCodeAt(1) - 0xDC00;
20842                     cc = (high * 0x400) + low + 0x10000;
20843                 }  else if (
20844                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20845                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20846                     (cc >= 0xf900 && cc < 0xfb00 )
20847                 ) {
20848                         return match;
20849                 }  
20850          
20851                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
20852                 return "&#" + cc + ";";
20853                 
20854                 
20855             });
20856             
20857             
20858              
20859             if(this.owner.fireEvent('beforesync', this, html) !== false){
20860                 this.el.dom.value = html;
20861                 this.owner.fireEvent('sync', this, html);
20862             }
20863         }
20864     },
20865
20866     /**
20867      * Protected method that will not generally be called directly. Pushes the value of the textarea
20868      * into the iframe editor.
20869      */
20870     pushValue : function(){
20871         if(this.initialized){
20872             var v = this.el.dom.value.trim();
20873             
20874 //            if(v.length < 1){
20875 //                v = '&#160;';
20876 //            }
20877             
20878             if(this.owner.fireEvent('beforepush', this, v) !== false){
20879                 var d = (this.doc.body || this.doc.documentElement);
20880                 d.innerHTML = v;
20881                 this.cleanUpPaste();
20882                 this.el.dom.value = d.innerHTML;
20883                 this.owner.fireEvent('push', this, v);
20884             }
20885         }
20886     },
20887
20888     // private
20889     deferFocus : function(){
20890         this.focus.defer(10, this);
20891     },
20892
20893     // doc'ed in Field
20894     focus : function(){
20895         if(this.win && !this.sourceEditMode){
20896             this.win.focus();
20897         }else{
20898             this.el.focus();
20899         }
20900     },
20901     
20902     assignDocWin: function()
20903     {
20904         var iframe = this.iframe;
20905         
20906          if(Roo.isIE){
20907             this.doc = iframe.contentWindow.document;
20908             this.win = iframe.contentWindow;
20909         } else {
20910 //            if (!Roo.get(this.frameId)) {
20911 //                return;
20912 //            }
20913 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20914 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20915             
20916             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20917                 return;
20918             }
20919             
20920             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20921             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20922         }
20923     },
20924     
20925     // private
20926     initEditor : function(){
20927         //console.log("INIT EDITOR");
20928         this.assignDocWin();
20929         
20930         
20931         
20932         this.doc.designMode="on";
20933         this.doc.open();
20934         this.doc.write(this.getDocMarkup());
20935         this.doc.close();
20936         
20937         var dbody = (this.doc.body || this.doc.documentElement);
20938         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20939         // this copies styles from the containing element into thsi one..
20940         // not sure why we need all of this..
20941         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20942         
20943         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20944         //ss['background-attachment'] = 'fixed'; // w3c
20945         dbody.bgProperties = 'fixed'; // ie
20946         //Roo.DomHelper.applyStyles(dbody, ss);
20947         Roo.EventManager.on(this.doc, {
20948             //'mousedown': this.onEditorEvent,
20949             'mouseup': this.onEditorEvent,
20950             'dblclick': this.onEditorEvent,
20951             'click': this.onEditorEvent,
20952             'keyup': this.onEditorEvent,
20953             buffer:100,
20954             scope: this
20955         });
20956         if(Roo.isGecko){
20957             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20958         }
20959         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20960             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20961         }
20962         this.initialized = true;
20963
20964         this.owner.fireEvent('initialize', this);
20965         this.pushValue();
20966     },
20967
20968     // private
20969     onDestroy : function(){
20970         
20971         
20972         
20973         if(this.rendered){
20974             
20975             //for (var i =0; i < this.toolbars.length;i++) {
20976             //    // fixme - ask toolbars for heights?
20977             //    this.toolbars[i].onDestroy();
20978            // }
20979             
20980             //this.wrap.dom.innerHTML = '';
20981             //this.wrap.remove();
20982         }
20983     },
20984
20985     // private
20986     onFirstFocus : function(){
20987         
20988         this.assignDocWin();
20989         
20990         
20991         this.activated = true;
20992          
20993     
20994         if(Roo.isGecko){ // prevent silly gecko errors
20995             this.win.focus();
20996             var s = this.win.getSelection();
20997             if(!s.focusNode || s.focusNode.nodeType != 3){
20998                 var r = s.getRangeAt(0);
20999                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21000                 r.collapse(true);
21001                 this.deferFocus();
21002             }
21003             try{
21004                 this.execCmd('useCSS', true);
21005                 this.execCmd('styleWithCSS', false);
21006             }catch(e){}
21007         }
21008         this.owner.fireEvent('activate', this);
21009     },
21010
21011     // private
21012     adjustFont: function(btn){
21013         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21014         //if(Roo.isSafari){ // safari
21015         //    adjust *= 2;
21016        // }
21017         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21018         if(Roo.isSafari){ // safari
21019             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21020             v =  (v < 10) ? 10 : v;
21021             v =  (v > 48) ? 48 : v;
21022             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21023             
21024         }
21025         
21026         
21027         v = Math.max(1, v+adjust);
21028         
21029         this.execCmd('FontSize', v  );
21030     },
21031
21032     onEditorEvent : function(e)
21033     {
21034         this.owner.fireEvent('editorevent', this, e);
21035       //  this.updateToolbar();
21036         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21037     },
21038
21039     insertTag : function(tg)
21040     {
21041         // could be a bit smarter... -> wrap the current selected tRoo..
21042         if (tg.toLowerCase() == 'span' ||
21043             tg.toLowerCase() == 'code' ||
21044             tg.toLowerCase() == 'sup' ||
21045             tg.toLowerCase() == 'sub' 
21046             ) {
21047             
21048             range = this.createRange(this.getSelection());
21049             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21050             wrappingNode.appendChild(range.extractContents());
21051             range.insertNode(wrappingNode);
21052
21053             return;
21054             
21055             
21056             
21057         }
21058         this.execCmd("formatblock",   tg);
21059         
21060     },
21061     
21062     insertText : function(txt)
21063     {
21064         
21065         
21066         var range = this.createRange();
21067         range.deleteContents();
21068                //alert(Sender.getAttribute('label'));
21069                
21070         range.insertNode(this.doc.createTextNode(txt));
21071     } ,
21072     
21073      
21074
21075     /**
21076      * Executes a Midas editor command on the editor document and performs necessary focus and
21077      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21078      * @param {String} cmd The Midas command
21079      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21080      */
21081     relayCmd : function(cmd, value){
21082         this.win.focus();
21083         this.execCmd(cmd, value);
21084         this.owner.fireEvent('editorevent', this);
21085         //this.updateToolbar();
21086         this.owner.deferFocus();
21087     },
21088
21089     /**
21090      * Executes a Midas editor command directly on the editor document.
21091      * For visual commands, you should use {@link #relayCmd} instead.
21092      * <b>This should only be called after the editor is initialized.</b>
21093      * @param {String} cmd The Midas command
21094      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21095      */
21096     execCmd : function(cmd, value){
21097         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21098         this.syncValue();
21099     },
21100  
21101  
21102    
21103     /**
21104      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21105      * to insert tRoo.
21106      * @param {String} text | dom node.. 
21107      */
21108     insertAtCursor : function(text)
21109     {
21110         
21111         if(!this.activated){
21112             return;
21113         }
21114         /*
21115         if(Roo.isIE){
21116             this.win.focus();
21117             var r = this.doc.selection.createRange();
21118             if(r){
21119                 r.collapse(true);
21120                 r.pasteHTML(text);
21121                 this.syncValue();
21122                 this.deferFocus();
21123             
21124             }
21125             return;
21126         }
21127         */
21128         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21129             this.win.focus();
21130             
21131             
21132             // from jquery ui (MIT licenced)
21133             var range, node;
21134             var win = this.win;
21135             
21136             if (win.getSelection && win.getSelection().getRangeAt) {
21137                 range = win.getSelection().getRangeAt(0);
21138                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21139                 range.insertNode(node);
21140             } else if (win.document.selection && win.document.selection.createRange) {
21141                 // no firefox support
21142                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21143                 win.document.selection.createRange().pasteHTML(txt);
21144             } else {
21145                 // no firefox support
21146                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21147                 this.execCmd('InsertHTML', txt);
21148             } 
21149             
21150             this.syncValue();
21151             
21152             this.deferFocus();
21153         }
21154     },
21155  // private
21156     mozKeyPress : function(e){
21157         if(e.ctrlKey){
21158             var c = e.getCharCode(), cmd;
21159           
21160             if(c > 0){
21161                 c = String.fromCharCode(c).toLowerCase();
21162                 switch(c){
21163                     case 'b':
21164                         cmd = 'bold';
21165                         break;
21166                     case 'i':
21167                         cmd = 'italic';
21168                         break;
21169                     
21170                     case 'u':
21171                         cmd = 'underline';
21172                         break;
21173                     
21174                     case 'v':
21175                         this.cleanUpPaste.defer(100, this);
21176                         return;
21177                         
21178                 }
21179                 if(cmd){
21180                     this.win.focus();
21181                     this.execCmd(cmd);
21182                     this.deferFocus();
21183                     e.preventDefault();
21184                 }
21185                 
21186             }
21187         }
21188     },
21189
21190     // private
21191     fixKeys : function(){ // load time branching for fastest keydown performance
21192         if(Roo.isIE){
21193             return function(e){
21194                 var k = e.getKey(), r;
21195                 if(k == e.TAB){
21196                     e.stopEvent();
21197                     r = this.doc.selection.createRange();
21198                     if(r){
21199                         r.collapse(true);
21200                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21201                         this.deferFocus();
21202                     }
21203                     return;
21204                 }
21205                 
21206                 if(k == e.ENTER){
21207                     r = this.doc.selection.createRange();
21208                     if(r){
21209                         var target = r.parentElement();
21210                         if(!target || target.tagName.toLowerCase() != 'li'){
21211                             e.stopEvent();
21212                             r.pasteHTML('<br />');
21213                             r.collapse(false);
21214                             r.select();
21215                         }
21216                     }
21217                 }
21218                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21219                     this.cleanUpPaste.defer(100, this);
21220                     return;
21221                 }
21222                 
21223                 
21224             };
21225         }else if(Roo.isOpera){
21226             return function(e){
21227                 var k = e.getKey();
21228                 if(k == e.TAB){
21229                     e.stopEvent();
21230                     this.win.focus();
21231                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21232                     this.deferFocus();
21233                 }
21234                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21235                     this.cleanUpPaste.defer(100, this);
21236                     return;
21237                 }
21238                 
21239             };
21240         }else if(Roo.isSafari){
21241             return function(e){
21242                 var k = e.getKey();
21243                 
21244                 if(k == e.TAB){
21245                     e.stopEvent();
21246                     this.execCmd('InsertText','\t');
21247                     this.deferFocus();
21248                     return;
21249                 }
21250                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21251                     this.cleanUpPaste.defer(100, this);
21252                     return;
21253                 }
21254                 
21255              };
21256         }
21257     }(),
21258     
21259     getAllAncestors: function()
21260     {
21261         var p = this.getSelectedNode();
21262         var a = [];
21263         if (!p) {
21264             a.push(p); // push blank onto stack..
21265             p = this.getParentElement();
21266         }
21267         
21268         
21269         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21270             a.push(p);
21271             p = p.parentNode;
21272         }
21273         a.push(this.doc.body);
21274         return a;
21275     },
21276     lastSel : false,
21277     lastSelNode : false,
21278     
21279     
21280     getSelection : function() 
21281     {
21282         this.assignDocWin();
21283         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21284     },
21285     
21286     getSelectedNode: function() 
21287     {
21288         // this may only work on Gecko!!!
21289         
21290         // should we cache this!!!!
21291         
21292         
21293         
21294          
21295         var range = this.createRange(this.getSelection()).cloneRange();
21296         
21297         if (Roo.isIE) {
21298             var parent = range.parentElement();
21299             while (true) {
21300                 var testRange = range.duplicate();
21301                 testRange.moveToElementText(parent);
21302                 if (testRange.inRange(range)) {
21303                     break;
21304                 }
21305                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21306                     break;
21307                 }
21308                 parent = parent.parentElement;
21309             }
21310             return parent;
21311         }
21312         
21313         // is ancestor a text element.
21314         var ac =  range.commonAncestorContainer;
21315         if (ac.nodeType == 3) {
21316             ac = ac.parentNode;
21317         }
21318         
21319         var ar = ac.childNodes;
21320          
21321         var nodes = [];
21322         var other_nodes = [];
21323         var has_other_nodes = false;
21324         for (var i=0;i<ar.length;i++) {
21325             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21326                 continue;
21327             }
21328             // fullly contained node.
21329             
21330             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21331                 nodes.push(ar[i]);
21332                 continue;
21333             }
21334             
21335             // probably selected..
21336             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21337                 other_nodes.push(ar[i]);
21338                 continue;
21339             }
21340             // outer..
21341             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21342                 continue;
21343             }
21344             
21345             
21346             has_other_nodes = true;
21347         }
21348         if (!nodes.length && other_nodes.length) {
21349             nodes= other_nodes;
21350         }
21351         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21352             return false;
21353         }
21354         
21355         return nodes[0];
21356     },
21357     createRange: function(sel)
21358     {
21359         // this has strange effects when using with 
21360         // top toolbar - not sure if it's a great idea.
21361         //this.editor.contentWindow.focus();
21362         if (typeof sel != "undefined") {
21363             try {
21364                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21365             } catch(e) {
21366                 return this.doc.createRange();
21367             }
21368         } else {
21369             return this.doc.createRange();
21370         }
21371     },
21372     getParentElement: function()
21373     {
21374         
21375         this.assignDocWin();
21376         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21377         
21378         var range = this.createRange(sel);
21379          
21380         try {
21381             var p = range.commonAncestorContainer;
21382             while (p.nodeType == 3) { // text node
21383                 p = p.parentNode;
21384             }
21385             return p;
21386         } catch (e) {
21387             return null;
21388         }
21389     
21390     },
21391     /***
21392      *
21393      * Range intersection.. the hard stuff...
21394      *  '-1' = before
21395      *  '0' = hits..
21396      *  '1' = after.
21397      *         [ -- selected range --- ]
21398      *   [fail]                        [fail]
21399      *
21400      *    basically..
21401      *      if end is before start or  hits it. fail.
21402      *      if start is after end or hits it fail.
21403      *
21404      *   if either hits (but other is outside. - then it's not 
21405      *   
21406      *    
21407      **/
21408     
21409     
21410     // @see http://www.thismuchiknow.co.uk/?p=64.
21411     rangeIntersectsNode : function(range, node)
21412     {
21413         var nodeRange = node.ownerDocument.createRange();
21414         try {
21415             nodeRange.selectNode(node);
21416         } catch (e) {
21417             nodeRange.selectNodeContents(node);
21418         }
21419     
21420         var rangeStartRange = range.cloneRange();
21421         rangeStartRange.collapse(true);
21422     
21423         var rangeEndRange = range.cloneRange();
21424         rangeEndRange.collapse(false);
21425     
21426         var nodeStartRange = nodeRange.cloneRange();
21427         nodeStartRange.collapse(true);
21428     
21429         var nodeEndRange = nodeRange.cloneRange();
21430         nodeEndRange.collapse(false);
21431     
21432         return rangeStartRange.compareBoundaryPoints(
21433                  Range.START_TO_START, nodeEndRange) == -1 &&
21434                rangeEndRange.compareBoundaryPoints(
21435                  Range.START_TO_START, nodeStartRange) == 1;
21436         
21437          
21438     },
21439     rangeCompareNode : function(range, node)
21440     {
21441         var nodeRange = node.ownerDocument.createRange();
21442         try {
21443             nodeRange.selectNode(node);
21444         } catch (e) {
21445             nodeRange.selectNodeContents(node);
21446         }
21447         
21448         
21449         range.collapse(true);
21450     
21451         nodeRange.collapse(true);
21452      
21453         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21454         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21455          
21456         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21457         
21458         var nodeIsBefore   =  ss == 1;
21459         var nodeIsAfter    = ee == -1;
21460         
21461         if (nodeIsBefore && nodeIsAfter) {
21462             return 0; // outer
21463         }
21464         if (!nodeIsBefore && nodeIsAfter) {
21465             return 1; //right trailed.
21466         }
21467         
21468         if (nodeIsBefore && !nodeIsAfter) {
21469             return 2;  // left trailed.
21470         }
21471         // fully contined.
21472         return 3;
21473     },
21474
21475     // private? - in a new class?
21476     cleanUpPaste :  function()
21477     {
21478         // cleans up the whole document..
21479         Roo.log('cleanuppaste');
21480         
21481         this.cleanUpChildren(this.doc.body);
21482         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21483         if (clean != this.doc.body.innerHTML) {
21484             this.doc.body.innerHTML = clean;
21485         }
21486         
21487     },
21488     
21489     cleanWordChars : function(input) {// change the chars to hex code
21490         var he = Roo.HtmlEditorCore;
21491         
21492         var output = input;
21493         Roo.each(he.swapCodes, function(sw) { 
21494             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21495             
21496             output = output.replace(swapper, sw[1]);
21497         });
21498         
21499         return output;
21500     },
21501     
21502     
21503     cleanUpChildren : function (n)
21504     {
21505         if (!n.childNodes.length) {
21506             return;
21507         }
21508         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21509            this.cleanUpChild(n.childNodes[i]);
21510         }
21511     },
21512     
21513     
21514         
21515     
21516     cleanUpChild : function (node)
21517     {
21518         var ed = this;
21519         //console.log(node);
21520         if (node.nodeName == "#text") {
21521             // clean up silly Windows -- stuff?
21522             return; 
21523         }
21524         if (node.nodeName == "#comment") {
21525             node.parentNode.removeChild(node);
21526             // clean up silly Windows -- stuff?
21527             return; 
21528         }
21529         var lcname = node.tagName.toLowerCase();
21530         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21531         // whitelist of tags..
21532         
21533         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21534             // remove node.
21535             node.parentNode.removeChild(node);
21536             return;
21537             
21538         }
21539         
21540         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21541         
21542         // spans with no attributes - just remove them..
21543         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
21544             remove_keep_children = true;
21545         }
21546         
21547         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21548         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21549         
21550         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21551         //    remove_keep_children = true;
21552         //}
21553         
21554         if (remove_keep_children) {
21555             this.cleanUpChildren(node);
21556             // inserts everything just before this node...
21557             while (node.childNodes.length) {
21558                 var cn = node.childNodes[0];
21559                 node.removeChild(cn);
21560                 node.parentNode.insertBefore(cn, node);
21561             }
21562             node.parentNode.removeChild(node);
21563             return;
21564         }
21565         
21566         if (!node.attributes || !node.attributes.length) {
21567             
21568           
21569             
21570             
21571             this.cleanUpChildren(node);
21572             return;
21573         }
21574         
21575         function cleanAttr(n,v)
21576         {
21577             
21578             if (v.match(/^\./) || v.match(/^\//)) {
21579                 return;
21580             }
21581             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21582                 return;
21583             }
21584             if (v.match(/^#/)) {
21585                 return;
21586             }
21587             if (v.match(/^\{/)) { // allow template editing.
21588                 return;
21589             }
21590 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21591             node.removeAttribute(n);
21592             
21593         }
21594         
21595         var cwhite = this.cwhite;
21596         var cblack = this.cblack;
21597             
21598         function cleanStyle(n,v)
21599         {
21600             if (v.match(/expression/)) { //XSS?? should we even bother..
21601                 node.removeAttribute(n);
21602                 return;
21603             }
21604             
21605             var parts = v.split(/;/);
21606             var clean = [];
21607             
21608             Roo.each(parts, function(p) {
21609                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21610                 if (!p.length) {
21611                     return true;
21612                 }
21613                 var l = p.split(':').shift().replace(/\s+/g,'');
21614                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21615                 
21616                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21617 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21618                     //node.removeAttribute(n);
21619                     return true;
21620                 }
21621                 //Roo.log()
21622                 // only allow 'c whitelisted system attributes'
21623                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21624 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21625                     //node.removeAttribute(n);
21626                     return true;
21627                 }
21628                 
21629                 
21630                  
21631                 
21632                 clean.push(p);
21633                 return true;
21634             });
21635             if (clean.length) { 
21636                 node.setAttribute(n, clean.join(';'));
21637             } else {
21638                 node.removeAttribute(n);
21639             }
21640             
21641         }
21642         
21643         
21644         for (var i = node.attributes.length-1; i > -1 ; i--) {
21645             var a = node.attributes[i];
21646             //console.log(a);
21647             
21648             if (a.name.toLowerCase().substr(0,2)=='on')  {
21649                 node.removeAttribute(a.name);
21650                 continue;
21651             }
21652             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21653                 node.removeAttribute(a.name);
21654                 continue;
21655             }
21656             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21657                 cleanAttr(a.name,a.value); // fixme..
21658                 continue;
21659             }
21660             if (a.name == 'style') {
21661                 cleanStyle(a.name,a.value);
21662                 continue;
21663             }
21664             /// clean up MS crap..
21665             // tecnically this should be a list of valid class'es..
21666             
21667             
21668             if (a.name == 'class') {
21669                 if (a.value.match(/^Mso/)) {
21670                     node.removeAttribute('class');
21671                 }
21672                 
21673                 if (a.value.match(/^body$/)) {
21674                     node.removeAttribute('class');
21675                 }
21676                 continue;
21677             }
21678             
21679             // style cleanup!?
21680             // class cleanup?
21681             
21682         }
21683         
21684         
21685         this.cleanUpChildren(node);
21686         
21687         
21688     },
21689     
21690     /**
21691      * Clean up MS wordisms...
21692      */
21693     cleanWord : function(node)
21694     {
21695         if (!node) {
21696             this.cleanWord(this.doc.body);
21697             return;
21698         }
21699         
21700         if(
21701                 node.nodeName == 'SPAN' &&
21702                 !node.hasAttributes() &&
21703                 node.childNodes.length == 1 &&
21704                 node.firstChild.nodeName == "#text"  
21705         ) {
21706             var textNode = node.firstChild;
21707             node.removeChild(textNode);
21708             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21709                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21710             }
21711             node.parentNode.insertBefore(textNode, node);
21712             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21713                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21714             }
21715             node.parentNode.removeChild(node);
21716         }
21717         
21718         if (node.nodeName == "#text") {
21719             // clean up silly Windows -- stuff?
21720             return; 
21721         }
21722         if (node.nodeName == "#comment") {
21723             node.parentNode.removeChild(node);
21724             // clean up silly Windows -- stuff?
21725             return; 
21726         }
21727         
21728         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21729             node.parentNode.removeChild(node);
21730             return;
21731         }
21732         //Roo.log(node.tagName);
21733         // remove - but keep children..
21734         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21735             //Roo.log('-- removed');
21736             while (node.childNodes.length) {
21737                 var cn = node.childNodes[0];
21738                 node.removeChild(cn);
21739                 node.parentNode.insertBefore(cn, node);
21740                 // move node to parent - and clean it..
21741                 this.cleanWord(cn);
21742             }
21743             node.parentNode.removeChild(node);
21744             /// no need to iterate chidlren = it's got none..
21745             //this.iterateChildren(node, this.cleanWord);
21746             return;
21747         }
21748         // clean styles
21749         if (node.className.length) {
21750             
21751             var cn = node.className.split(/\W+/);
21752             var cna = [];
21753             Roo.each(cn, function(cls) {
21754                 if (cls.match(/Mso[a-zA-Z]+/)) {
21755                     return;
21756                 }
21757                 cna.push(cls);
21758             });
21759             node.className = cna.length ? cna.join(' ') : '';
21760             if (!cna.length) {
21761                 node.removeAttribute("class");
21762             }
21763         }
21764         
21765         if (node.hasAttribute("lang")) {
21766             node.removeAttribute("lang");
21767         }
21768         
21769         if (node.hasAttribute("style")) {
21770             
21771             var styles = node.getAttribute("style").split(";");
21772             var nstyle = [];
21773             Roo.each(styles, function(s) {
21774                 if (!s.match(/:/)) {
21775                     return;
21776                 }
21777                 var kv = s.split(":");
21778                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21779                     return;
21780                 }
21781                 // what ever is left... we allow.
21782                 nstyle.push(s);
21783             });
21784             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21785             if (!nstyle.length) {
21786                 node.removeAttribute('style');
21787             }
21788         }
21789         this.iterateChildren(node, this.cleanWord);
21790         
21791         
21792         
21793     },
21794     /**
21795      * iterateChildren of a Node, calling fn each time, using this as the scole..
21796      * @param {DomNode} node node to iterate children of.
21797      * @param {Function} fn method of this class to call on each item.
21798      */
21799     iterateChildren : function(node, fn)
21800     {
21801         if (!node.childNodes.length) {
21802                 return;
21803         }
21804         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21805            fn.call(this, node.childNodes[i])
21806         }
21807     },
21808     
21809     
21810     /**
21811      * cleanTableWidths.
21812      *
21813      * Quite often pasting from word etc.. results in tables with column and widths.
21814      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21815      *
21816      */
21817     cleanTableWidths : function(node)
21818     {
21819          
21820          
21821         if (!node) {
21822             this.cleanTableWidths(this.doc.body);
21823             return;
21824         }
21825         
21826         // ignore list...
21827         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21828             return; 
21829         }
21830         Roo.log(node.tagName);
21831         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21832             this.iterateChildren(node, this.cleanTableWidths);
21833             return;
21834         }
21835         if (node.hasAttribute('width')) {
21836             node.removeAttribute('width');
21837         }
21838         
21839          
21840         if (node.hasAttribute("style")) {
21841             // pretty basic...
21842             
21843             var styles = node.getAttribute("style").split(";");
21844             var nstyle = [];
21845             Roo.each(styles, function(s) {
21846                 if (!s.match(/:/)) {
21847                     return;
21848                 }
21849                 var kv = s.split(":");
21850                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21851                     return;
21852                 }
21853                 // what ever is left... we allow.
21854                 nstyle.push(s);
21855             });
21856             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21857             if (!nstyle.length) {
21858                 node.removeAttribute('style');
21859             }
21860         }
21861         
21862         this.iterateChildren(node, this.cleanTableWidths);
21863         
21864         
21865     },
21866     
21867     
21868     
21869     
21870     domToHTML : function(currentElement, depth, nopadtext) {
21871         
21872         depth = depth || 0;
21873         nopadtext = nopadtext || false;
21874     
21875         if (!currentElement) {
21876             return this.domToHTML(this.doc.body);
21877         }
21878         
21879         //Roo.log(currentElement);
21880         var j;
21881         var allText = false;
21882         var nodeName = currentElement.nodeName;
21883         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21884         
21885         if  (nodeName == '#text') {
21886             
21887             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21888         }
21889         
21890         
21891         var ret = '';
21892         if (nodeName != 'BODY') {
21893              
21894             var i = 0;
21895             // Prints the node tagName, such as <A>, <IMG>, etc
21896             if (tagName) {
21897                 var attr = [];
21898                 for(i = 0; i < currentElement.attributes.length;i++) {
21899                     // quoting?
21900                     var aname = currentElement.attributes.item(i).name;
21901                     if (!currentElement.attributes.item(i).value.length) {
21902                         continue;
21903                     }
21904                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21905                 }
21906                 
21907                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21908             } 
21909             else {
21910                 
21911                 // eack
21912             }
21913         } else {
21914             tagName = false;
21915         }
21916         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21917             return ret;
21918         }
21919         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21920             nopadtext = true;
21921         }
21922         
21923         
21924         // Traverse the tree
21925         i = 0;
21926         var currentElementChild = currentElement.childNodes.item(i);
21927         var allText = true;
21928         var innerHTML  = '';
21929         lastnode = '';
21930         while (currentElementChild) {
21931             // Formatting code (indent the tree so it looks nice on the screen)
21932             var nopad = nopadtext;
21933             if (lastnode == 'SPAN') {
21934                 nopad  = true;
21935             }
21936             // text
21937             if  (currentElementChild.nodeName == '#text') {
21938                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21939                 toadd = nopadtext ? toadd : toadd.trim();
21940                 if (!nopad && toadd.length > 80) {
21941                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21942                 }
21943                 innerHTML  += toadd;
21944                 
21945                 i++;
21946                 currentElementChild = currentElement.childNodes.item(i);
21947                 lastNode = '';
21948                 continue;
21949             }
21950             allText = false;
21951             
21952             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21953                 
21954             // Recursively traverse the tree structure of the child node
21955             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21956             lastnode = currentElementChild.nodeName;
21957             i++;
21958             currentElementChild=currentElement.childNodes.item(i);
21959         }
21960         
21961         ret += innerHTML;
21962         
21963         if (!allText) {
21964                 // The remaining code is mostly for formatting the tree
21965             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21966         }
21967         
21968         
21969         if (tagName) {
21970             ret+= "</"+tagName+">";
21971         }
21972         return ret;
21973         
21974     },
21975         
21976     applyBlacklists : function()
21977     {
21978         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21979         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21980         
21981         this.white = [];
21982         this.black = [];
21983         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21984             if (b.indexOf(tag) > -1) {
21985                 return;
21986             }
21987             this.white.push(tag);
21988             
21989         }, this);
21990         
21991         Roo.each(w, function(tag) {
21992             if (b.indexOf(tag) > -1) {
21993                 return;
21994             }
21995             if (this.white.indexOf(tag) > -1) {
21996                 return;
21997             }
21998             this.white.push(tag);
21999             
22000         }, this);
22001         
22002         
22003         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22004             if (w.indexOf(tag) > -1) {
22005                 return;
22006             }
22007             this.black.push(tag);
22008             
22009         }, this);
22010         
22011         Roo.each(b, function(tag) {
22012             if (w.indexOf(tag) > -1) {
22013                 return;
22014             }
22015             if (this.black.indexOf(tag) > -1) {
22016                 return;
22017             }
22018             this.black.push(tag);
22019             
22020         }, this);
22021         
22022         
22023         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22024         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22025         
22026         this.cwhite = [];
22027         this.cblack = [];
22028         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22029             if (b.indexOf(tag) > -1) {
22030                 return;
22031             }
22032             this.cwhite.push(tag);
22033             
22034         }, this);
22035         
22036         Roo.each(w, function(tag) {
22037             if (b.indexOf(tag) > -1) {
22038                 return;
22039             }
22040             if (this.cwhite.indexOf(tag) > -1) {
22041                 return;
22042             }
22043             this.cwhite.push(tag);
22044             
22045         }, this);
22046         
22047         
22048         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22049             if (w.indexOf(tag) > -1) {
22050                 return;
22051             }
22052             this.cblack.push(tag);
22053             
22054         }, this);
22055         
22056         Roo.each(b, function(tag) {
22057             if (w.indexOf(tag) > -1) {
22058                 return;
22059             }
22060             if (this.cblack.indexOf(tag) > -1) {
22061                 return;
22062             }
22063             this.cblack.push(tag);
22064             
22065         }, this);
22066     },
22067     
22068     setStylesheets : function(stylesheets)
22069     {
22070         if(typeof(stylesheets) == 'string'){
22071             Roo.get(this.iframe.contentDocument.head).createChild({
22072                 tag : 'link',
22073                 rel : 'stylesheet',
22074                 type : 'text/css',
22075                 href : stylesheets
22076             });
22077             
22078             return;
22079         }
22080         var _this = this;
22081      
22082         Roo.each(stylesheets, function(s) {
22083             if(!s.length){
22084                 return;
22085             }
22086             
22087             Roo.get(_this.iframe.contentDocument.head).createChild({
22088                 tag : 'link',
22089                 rel : 'stylesheet',
22090                 type : 'text/css',
22091                 href : s
22092             });
22093         });
22094
22095         
22096     },
22097     
22098     removeStylesheets : function()
22099     {
22100         var _this = this;
22101         
22102         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22103             s.remove();
22104         });
22105     },
22106     
22107     setStyle : function(style)
22108     {
22109         Roo.get(this.iframe.contentDocument.head).createChild({
22110             tag : 'style',
22111             type : 'text/css',
22112             html : style
22113         });
22114
22115         return;
22116     }
22117     
22118     // hide stuff that is not compatible
22119     /**
22120      * @event blur
22121      * @hide
22122      */
22123     /**
22124      * @event change
22125      * @hide
22126      */
22127     /**
22128      * @event focus
22129      * @hide
22130      */
22131     /**
22132      * @event specialkey
22133      * @hide
22134      */
22135     /**
22136      * @cfg {String} fieldClass @hide
22137      */
22138     /**
22139      * @cfg {String} focusClass @hide
22140      */
22141     /**
22142      * @cfg {String} autoCreate @hide
22143      */
22144     /**
22145      * @cfg {String} inputType @hide
22146      */
22147     /**
22148      * @cfg {String} invalidClass @hide
22149      */
22150     /**
22151      * @cfg {String} invalidText @hide
22152      */
22153     /**
22154      * @cfg {String} msgFx @hide
22155      */
22156     /**
22157      * @cfg {String} validateOnBlur @hide
22158      */
22159 });
22160
22161 Roo.HtmlEditorCore.white = [
22162         'area', 'br', 'img', 'input', 'hr', 'wbr',
22163         
22164        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22165        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22166        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22167        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22168        'table',   'ul',         'xmp', 
22169        
22170        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22171       'thead',   'tr', 
22172      
22173       'dir', 'menu', 'ol', 'ul', 'dl',
22174        
22175       'embed',  'object'
22176 ];
22177
22178
22179 Roo.HtmlEditorCore.black = [
22180     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22181         'applet', // 
22182         'base',   'basefont', 'bgsound', 'blink',  'body', 
22183         'frame',  'frameset', 'head',    'html',   'ilayer', 
22184         'iframe', 'layer',  'link',     'meta',    'object',   
22185         'script', 'style' ,'title',  'xml' // clean later..
22186 ];
22187 Roo.HtmlEditorCore.clean = [
22188     'script', 'style', 'title', 'xml'
22189 ];
22190 Roo.HtmlEditorCore.remove = [
22191     'font'
22192 ];
22193 // attributes..
22194
22195 Roo.HtmlEditorCore.ablack = [
22196     'on'
22197 ];
22198     
22199 Roo.HtmlEditorCore.aclean = [ 
22200     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22201 ];
22202
22203 // protocols..
22204 Roo.HtmlEditorCore.pwhite= [
22205         'http',  'https',  'mailto'
22206 ];
22207
22208 // white listed style attributes.
22209 Roo.HtmlEditorCore.cwhite= [
22210       //  'text-align', /// default is to allow most things..
22211       
22212          
22213 //        'font-size'//??
22214 ];
22215
22216 // black listed style attributes.
22217 Roo.HtmlEditorCore.cblack= [
22218       //  'font-size' -- this can be set by the project 
22219 ];
22220
22221
22222 Roo.HtmlEditorCore.swapCodes   =[ 
22223     [    8211, "&#8211;" ], 
22224     [    8212, "&#8212;" ], 
22225     [    8216,  "'" ],  
22226     [    8217, "'" ],  
22227     [    8220, '"' ],  
22228     [    8221, '"' ],  
22229     [    8226, "*" ],  
22230     [    8230, "..." ]
22231 ]; 
22232
22233     //<script type="text/javascript">
22234
22235 /*
22236  * Ext JS Library 1.1.1
22237  * Copyright(c) 2006-2007, Ext JS, LLC.
22238  * Licence LGPL
22239  * 
22240  */
22241  
22242  
22243 Roo.form.HtmlEditor = function(config){
22244     
22245     
22246     
22247     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22248     
22249     if (!this.toolbars) {
22250         this.toolbars = [];
22251     }
22252     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22253     
22254     
22255 };
22256
22257 /**
22258  * @class Roo.form.HtmlEditor
22259  * @extends Roo.form.Field
22260  * Provides a lightweight HTML Editor component.
22261  *
22262  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22263  * 
22264  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22265  * supported by this editor.</b><br/><br/>
22266  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22267  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22268  */
22269 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22270     /**
22271      * @cfg {Boolean} clearUp
22272      */
22273     clearUp : true,
22274       /**
22275      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22276      */
22277     toolbars : false,
22278    
22279      /**
22280      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22281      *                        Roo.resizable.
22282      */
22283     resizable : false,
22284      /**
22285      * @cfg {Number} height (in pixels)
22286      */   
22287     height: 300,
22288    /**
22289      * @cfg {Number} width (in pixels)
22290      */   
22291     width: 500,
22292     
22293     /**
22294      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22295      * 
22296      */
22297     stylesheets: false,
22298     
22299     
22300      /**
22301      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22302      * 
22303      */
22304     cblack: false,
22305     /**
22306      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22307      * 
22308      */
22309     cwhite: false,
22310     
22311      /**
22312      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22313      * 
22314      */
22315     black: false,
22316     /**
22317      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22318      * 
22319      */
22320     white: false,
22321     
22322     // id of frame..
22323     frameId: false,
22324     
22325     // private properties
22326     validationEvent : false,
22327     deferHeight: true,
22328     initialized : false,
22329     activated : false,
22330     
22331     onFocus : Roo.emptyFn,
22332     iframePad:3,
22333     hideMode:'offsets',
22334     
22335     actionMode : 'container', // defaults to hiding it...
22336     
22337     defaultAutoCreate : { // modified by initCompnoent..
22338         tag: "textarea",
22339         style:"width:500px;height:300px;",
22340         autocomplete: "new-password"
22341     },
22342
22343     // private
22344     initComponent : function(){
22345         this.addEvents({
22346             /**
22347              * @event initialize
22348              * Fires when the editor is fully initialized (including the iframe)
22349              * @param {HtmlEditor} this
22350              */
22351             initialize: true,
22352             /**
22353              * @event activate
22354              * Fires when the editor is first receives the focus. Any insertion must wait
22355              * until after this event.
22356              * @param {HtmlEditor} this
22357              */
22358             activate: true,
22359              /**
22360              * @event beforesync
22361              * Fires before the textarea is updated with content from the editor iframe. Return false
22362              * to cancel the sync.
22363              * @param {HtmlEditor} this
22364              * @param {String} html
22365              */
22366             beforesync: true,
22367              /**
22368              * @event beforepush
22369              * Fires before the iframe editor is updated with content from the textarea. Return false
22370              * to cancel the push.
22371              * @param {HtmlEditor} this
22372              * @param {String} html
22373              */
22374             beforepush: true,
22375              /**
22376              * @event sync
22377              * Fires when the textarea is updated with content from the editor iframe.
22378              * @param {HtmlEditor} this
22379              * @param {String} html
22380              */
22381             sync: true,
22382              /**
22383              * @event push
22384              * Fires when the iframe editor is updated with content from the textarea.
22385              * @param {HtmlEditor} this
22386              * @param {String} html
22387              */
22388             push: true,
22389              /**
22390              * @event editmodechange
22391              * Fires when the editor switches edit modes
22392              * @param {HtmlEditor} this
22393              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22394              */
22395             editmodechange: true,
22396             /**
22397              * @event editorevent
22398              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22399              * @param {HtmlEditor} this
22400              */
22401             editorevent: true,
22402             /**
22403              * @event firstfocus
22404              * Fires when on first focus - needed by toolbars..
22405              * @param {HtmlEditor} this
22406              */
22407             firstfocus: true,
22408             /**
22409              * @event autosave
22410              * Auto save the htmlEditor value as a file into Events
22411              * @param {HtmlEditor} this
22412              */
22413             autosave: true,
22414             /**
22415              * @event savedpreview
22416              * preview the saved version of htmlEditor
22417              * @param {HtmlEditor} this
22418              */
22419             savedpreview: true,
22420             
22421             /**
22422             * @event stylesheetsclick
22423             * Fires when press the Sytlesheets button
22424             * @param {Roo.HtmlEditorCore} this
22425             */
22426             stylesheetsclick: true
22427         });
22428         this.defaultAutoCreate =  {
22429             tag: "textarea",
22430             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22431             autocomplete: "new-password"
22432         };
22433     },
22434
22435     /**
22436      * Protected method that will not generally be called directly. It
22437      * is called when the editor creates its toolbar. Override this method if you need to
22438      * add custom toolbar buttons.
22439      * @param {HtmlEditor} editor
22440      */
22441     createToolbar : function(editor){
22442         Roo.log("create toolbars");
22443         if (!editor.toolbars || !editor.toolbars.length) {
22444             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22445         }
22446         
22447         for (var i =0 ; i < editor.toolbars.length;i++) {
22448             editor.toolbars[i] = Roo.factory(
22449                     typeof(editor.toolbars[i]) == 'string' ?
22450                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22451                 Roo.form.HtmlEditor);
22452             editor.toolbars[i].init(editor);
22453         }
22454          
22455         
22456     },
22457
22458      
22459     // private
22460     onRender : function(ct, position)
22461     {
22462         var _t = this;
22463         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22464         
22465         this.wrap = this.el.wrap({
22466             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22467         });
22468         
22469         this.editorcore.onRender(ct, position);
22470          
22471         if (this.resizable) {
22472             this.resizeEl = new Roo.Resizable(this.wrap, {
22473                 pinned : true,
22474                 wrap: true,
22475                 dynamic : true,
22476                 minHeight : this.height,
22477                 height: this.height,
22478                 handles : this.resizable,
22479                 width: this.width,
22480                 listeners : {
22481                     resize : function(r, w, h) {
22482                         _t.onResize(w,h); // -something
22483                     }
22484                 }
22485             });
22486             
22487         }
22488         this.createToolbar(this);
22489        
22490         
22491         if(!this.width){
22492             this.setSize(this.wrap.getSize());
22493         }
22494         if (this.resizeEl) {
22495             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22496             // should trigger onReize..
22497         }
22498         
22499         this.keyNav = new Roo.KeyNav(this.el, {
22500             
22501             "tab" : function(e){
22502                 e.preventDefault();
22503                 
22504                 var value = this.getValue();
22505                 
22506                 var start = this.el.dom.selectionStart;
22507                 var end = this.el.dom.selectionEnd;
22508                 
22509                 if(!e.shiftKey){
22510                     
22511                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22512                     this.el.dom.setSelectionRange(end + 1, end + 1);
22513                     return;
22514                 }
22515                 
22516                 var f = value.substring(0, start).split("\t");
22517                 
22518                 if(f.pop().length != 0){
22519                     return;
22520                 }
22521                 
22522                 this.setValue(f.join("\t") + value.substring(end));
22523                 this.el.dom.setSelectionRange(start - 1, start - 1);
22524                 
22525             },
22526             
22527             "home" : function(e){
22528                 e.preventDefault();
22529                 
22530                 var curr = this.el.dom.selectionStart;
22531                 var lines = this.getValue().split("\n");
22532                 
22533                 if(!lines.length){
22534                     return;
22535                 }
22536                 
22537                 if(e.ctrlKey){
22538                     this.el.dom.setSelectionRange(0, 0);
22539                     return;
22540                 }
22541                 
22542                 var pos = 0;
22543                 
22544                 for (var i = 0; i < lines.length;i++) {
22545                     pos += lines[i].length;
22546                     
22547                     if(i != 0){
22548                         pos += 1;
22549                     }
22550                     
22551                     if(pos < curr){
22552                         continue;
22553                     }
22554                     
22555                     pos -= lines[i].length;
22556                     
22557                     break;
22558                 }
22559                 
22560                 if(!e.shiftKey){
22561                     this.el.dom.setSelectionRange(pos, pos);
22562                     return;
22563                 }
22564                 
22565                 this.el.dom.selectionStart = pos;
22566                 this.el.dom.selectionEnd = curr;
22567             },
22568             
22569             "end" : function(e){
22570                 e.preventDefault();
22571                 
22572                 var curr = this.el.dom.selectionStart;
22573                 var lines = this.getValue().split("\n");
22574                 
22575                 if(!lines.length){
22576                     return;
22577                 }
22578                 
22579                 if(e.ctrlKey){
22580                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22581                     return;
22582                 }
22583                 
22584                 var pos = 0;
22585                 
22586                 for (var i = 0; i < lines.length;i++) {
22587                     
22588                     pos += lines[i].length;
22589                     
22590                     if(i != 0){
22591                         pos += 1;
22592                     }
22593                     
22594                     if(pos < curr){
22595                         continue;
22596                     }
22597                     
22598                     break;
22599                 }
22600                 
22601                 if(!e.shiftKey){
22602                     this.el.dom.setSelectionRange(pos, pos);
22603                     return;
22604                 }
22605                 
22606                 this.el.dom.selectionStart = curr;
22607                 this.el.dom.selectionEnd = pos;
22608             },
22609
22610             scope : this,
22611
22612             doRelay : function(foo, bar, hname){
22613                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22614             },
22615
22616             forceKeyDown: true
22617         });
22618         
22619 //        if(this.autosave && this.w){
22620 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22621 //        }
22622     },
22623
22624     // private
22625     onResize : function(w, h)
22626     {
22627         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22628         var ew = false;
22629         var eh = false;
22630         
22631         if(this.el ){
22632             if(typeof w == 'number'){
22633                 var aw = w - this.wrap.getFrameWidth('lr');
22634                 this.el.setWidth(this.adjustWidth('textarea', aw));
22635                 ew = aw;
22636             }
22637             if(typeof h == 'number'){
22638                 var tbh = 0;
22639                 for (var i =0; i < this.toolbars.length;i++) {
22640                     // fixme - ask toolbars for heights?
22641                     tbh += this.toolbars[i].tb.el.getHeight();
22642                     if (this.toolbars[i].footer) {
22643                         tbh += this.toolbars[i].footer.el.getHeight();
22644                     }
22645                 }
22646                 
22647                 
22648                 
22649                 
22650                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22651                 ah -= 5; // knock a few pixes off for look..
22652 //                Roo.log(ah);
22653                 this.el.setHeight(this.adjustWidth('textarea', ah));
22654                 var eh = ah;
22655             }
22656         }
22657         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22658         this.editorcore.onResize(ew,eh);
22659         
22660     },
22661
22662     /**
22663      * Toggles the editor between standard and source edit mode.
22664      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22665      */
22666     toggleSourceEdit : function(sourceEditMode)
22667     {
22668         this.editorcore.toggleSourceEdit(sourceEditMode);
22669         
22670         if(this.editorcore.sourceEditMode){
22671             Roo.log('editor - showing textarea');
22672             
22673 //            Roo.log('in');
22674 //            Roo.log(this.syncValue());
22675             this.editorcore.syncValue();
22676             this.el.removeClass('x-hidden');
22677             this.el.dom.removeAttribute('tabIndex');
22678             this.el.focus();
22679             
22680             for (var i = 0; i < this.toolbars.length; i++) {
22681                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22682                     this.toolbars[i].tb.hide();
22683                     this.toolbars[i].footer.hide();
22684                 }
22685             }
22686             
22687         }else{
22688             Roo.log('editor - hiding textarea');
22689 //            Roo.log('out')
22690 //            Roo.log(this.pushValue()); 
22691             this.editorcore.pushValue();
22692             
22693             this.el.addClass('x-hidden');
22694             this.el.dom.setAttribute('tabIndex', -1);
22695             
22696             for (var i = 0; i < this.toolbars.length; i++) {
22697                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22698                     this.toolbars[i].tb.show();
22699                     this.toolbars[i].footer.show();
22700                 }
22701             }
22702             
22703             //this.deferFocus();
22704         }
22705         
22706         this.setSize(this.wrap.getSize());
22707         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22708         
22709         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22710     },
22711  
22712     // private (for BoxComponent)
22713     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22714
22715     // private (for BoxComponent)
22716     getResizeEl : function(){
22717         return this.wrap;
22718     },
22719
22720     // private (for BoxComponent)
22721     getPositionEl : function(){
22722         return this.wrap;
22723     },
22724
22725     // private
22726     initEvents : function(){
22727         this.originalValue = this.getValue();
22728     },
22729
22730     /**
22731      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22732      * @method
22733      */
22734     markInvalid : Roo.emptyFn,
22735     /**
22736      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22737      * @method
22738      */
22739     clearInvalid : Roo.emptyFn,
22740
22741     setValue : function(v){
22742         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22743         this.editorcore.pushValue();
22744     },
22745
22746      
22747     // private
22748     deferFocus : function(){
22749         this.focus.defer(10, this);
22750     },
22751
22752     // doc'ed in Field
22753     focus : function(){
22754         this.editorcore.focus();
22755         
22756     },
22757       
22758
22759     // private
22760     onDestroy : function(){
22761         
22762         
22763         
22764         if(this.rendered){
22765             
22766             for (var i =0; i < this.toolbars.length;i++) {
22767                 // fixme - ask toolbars for heights?
22768                 this.toolbars[i].onDestroy();
22769             }
22770             
22771             this.wrap.dom.innerHTML = '';
22772             this.wrap.remove();
22773         }
22774     },
22775
22776     // private
22777     onFirstFocus : function(){
22778         //Roo.log("onFirstFocus");
22779         this.editorcore.onFirstFocus();
22780          for (var i =0; i < this.toolbars.length;i++) {
22781             this.toolbars[i].onFirstFocus();
22782         }
22783         
22784     },
22785     
22786     // private
22787     syncValue : function()
22788     {
22789         this.editorcore.syncValue();
22790     },
22791     
22792     pushValue : function()
22793     {
22794         this.editorcore.pushValue();
22795     },
22796     
22797     setStylesheets : function(stylesheets)
22798     {
22799         this.editorcore.setStylesheets(stylesheets);
22800     },
22801     
22802     removeStylesheets : function()
22803     {
22804         this.editorcore.removeStylesheets();
22805     }
22806      
22807     
22808     // hide stuff that is not compatible
22809     /**
22810      * @event blur
22811      * @hide
22812      */
22813     /**
22814      * @event change
22815      * @hide
22816      */
22817     /**
22818      * @event focus
22819      * @hide
22820      */
22821     /**
22822      * @event specialkey
22823      * @hide
22824      */
22825     /**
22826      * @cfg {String} fieldClass @hide
22827      */
22828     /**
22829      * @cfg {String} focusClass @hide
22830      */
22831     /**
22832      * @cfg {String} autoCreate @hide
22833      */
22834     /**
22835      * @cfg {String} inputType @hide
22836      */
22837     /**
22838      * @cfg {String} invalidClass @hide
22839      */
22840     /**
22841      * @cfg {String} invalidText @hide
22842      */
22843     /**
22844      * @cfg {String} msgFx @hide
22845      */
22846     /**
22847      * @cfg {String} validateOnBlur @hide
22848      */
22849 });
22850  
22851     // <script type="text/javascript">
22852 /*
22853  * Based on
22854  * Ext JS Library 1.1.1
22855  * Copyright(c) 2006-2007, Ext JS, LLC.
22856  *  
22857  
22858  */
22859
22860 /**
22861  * @class Roo.form.HtmlEditorToolbar1
22862  * Basic Toolbar
22863  * 
22864  * Usage:
22865  *
22866  new Roo.form.HtmlEditor({
22867     ....
22868     toolbars : [
22869         new Roo.form.HtmlEditorToolbar1({
22870             disable : { fonts: 1 , format: 1, ..., ... , ...],
22871             btns : [ .... ]
22872         })
22873     }
22874      
22875  * 
22876  * @cfg {Object} disable List of elements to disable..
22877  * @cfg {Array} btns List of additional buttons.
22878  * 
22879  * 
22880  * NEEDS Extra CSS? 
22881  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22882  */
22883  
22884 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22885 {
22886     
22887     Roo.apply(this, config);
22888     
22889     // default disabled, based on 'good practice'..
22890     this.disable = this.disable || {};
22891     Roo.applyIf(this.disable, {
22892         fontSize : true,
22893         colors : true,
22894         specialElements : true
22895     });
22896     
22897     
22898     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22899     // dont call parent... till later.
22900 }
22901
22902 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22903     
22904     tb: false,
22905     
22906     rendered: false,
22907     
22908     editor : false,
22909     editorcore : false,
22910     /**
22911      * @cfg {Object} disable  List of toolbar elements to disable
22912          
22913      */
22914     disable : false,
22915     
22916     
22917      /**
22918      * @cfg {String} createLinkText The default text for the create link prompt
22919      */
22920     createLinkText : 'Please enter the URL for the link:',
22921     /**
22922      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22923      */
22924     defaultLinkValue : 'http:/'+'/',
22925    
22926     
22927       /**
22928      * @cfg {Array} fontFamilies An array of available font families
22929      */
22930     fontFamilies : [
22931         'Arial',
22932         'Courier New',
22933         'Tahoma',
22934         'Times New Roman',
22935         'Verdana'
22936     ],
22937     
22938     specialChars : [
22939            "&#169;",
22940           "&#174;",     
22941           "&#8482;",    
22942           "&#163;" ,    
22943          // "&#8212;",    
22944           "&#8230;",    
22945           "&#247;" ,    
22946         //  "&#225;" ,     ?? a acute?
22947            "&#8364;"    , //Euro
22948        //   "&#8220;"    ,
22949         //  "&#8221;"    ,
22950         //  "&#8226;"    ,
22951           "&#176;"  //   , // degrees
22952
22953          // "&#233;"     , // e ecute
22954          // "&#250;"     , // u ecute?
22955     ],
22956     
22957     specialElements : [
22958         {
22959             text: "Insert Table",
22960             xtype: 'MenuItem',
22961             xns : Roo.Menu,
22962             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
22963                 
22964         },
22965         {    
22966             text: "Insert Image",
22967             xtype: 'MenuItem',
22968             xns : Roo.Menu,
22969             ihtml : '<img src="about:blank"/>'
22970             
22971         }
22972         
22973          
22974     ],
22975     
22976     
22977     inputElements : [ 
22978             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
22979             "input:submit", "input:button", "select", "textarea", "label" ],
22980     formats : [
22981         ["p"] ,  
22982         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
22983         ["pre"],[ "code"], 
22984         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22985         ['div'],['span'],
22986         ['sup'],['sub']
22987     ],
22988     
22989     cleanStyles : [
22990         "font-size"
22991     ],
22992      /**
22993      * @cfg {String} defaultFont default font to use.
22994      */
22995     defaultFont: 'tahoma',
22996    
22997     fontSelect : false,
22998     
22999     
23000     formatCombo : false,
23001     
23002     init : function(editor)
23003     {
23004         this.editor = editor;
23005         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23006         var editorcore = this.editorcore;
23007         
23008         var _t = this;
23009         
23010         var fid = editorcore.frameId;
23011         var etb = this;
23012         function btn(id, toggle, handler){
23013             var xid = fid + '-'+ id ;
23014             return {
23015                 id : xid,
23016                 cmd : id,
23017                 cls : 'x-btn-icon x-edit-'+id,
23018                 enableToggle:toggle !== false,
23019                 scope: _t, // was editor...
23020                 handler:handler||_t.relayBtnCmd,
23021                 clickEvent:'mousedown',
23022                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23023                 tabIndex:-1
23024             };
23025         }
23026         
23027         
23028         
23029         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23030         this.tb = tb;
23031          // stop form submits
23032         tb.el.on('click', function(e){
23033             e.preventDefault(); // what does this do?
23034         });
23035
23036         if(!this.disable.font) { // && !Roo.isSafari){
23037             /* why no safari for fonts 
23038             editor.fontSelect = tb.el.createChild({
23039                 tag:'select',
23040                 tabIndex: -1,
23041                 cls:'x-font-select',
23042                 html: this.createFontOptions()
23043             });
23044             
23045             editor.fontSelect.on('change', function(){
23046                 var font = editor.fontSelect.dom.value;
23047                 editor.relayCmd('fontname', font);
23048                 editor.deferFocus();
23049             }, editor);
23050             
23051             tb.add(
23052                 editor.fontSelect.dom,
23053                 '-'
23054             );
23055             */
23056             
23057         };
23058         if(!this.disable.formats){
23059             this.formatCombo = new Roo.form.ComboBox({
23060                 store: new Roo.data.SimpleStore({
23061                     id : 'tag',
23062                     fields: ['tag'],
23063                     data : this.formats // from states.js
23064                 }),
23065                 blockFocus : true,
23066                 name : '',
23067                 //autoCreate : {tag: "div",  size: "20"},
23068                 displayField:'tag',
23069                 typeAhead: false,
23070                 mode: 'local',
23071                 editable : false,
23072                 triggerAction: 'all',
23073                 emptyText:'Add tag',
23074                 selectOnFocus:true,
23075                 width:135,
23076                 listeners : {
23077                     'select': function(c, r, i) {
23078                         editorcore.insertTag(r.get('tag'));
23079                         editor.focus();
23080                     }
23081                 }
23082
23083             });
23084             tb.addField(this.formatCombo);
23085             
23086         }
23087         
23088         if(!this.disable.format){
23089             tb.add(
23090                 btn('bold'),
23091                 btn('italic'),
23092                 btn('underline'),
23093                 btn('strikethrough')
23094             );
23095         };
23096         if(!this.disable.fontSize){
23097             tb.add(
23098                 '-',
23099                 
23100                 
23101                 btn('increasefontsize', false, editorcore.adjustFont),
23102                 btn('decreasefontsize', false, editorcore.adjustFont)
23103             );
23104         };
23105         
23106         
23107         if(!this.disable.colors){
23108             tb.add(
23109                 '-', {
23110                     id:editorcore.frameId +'-forecolor',
23111                     cls:'x-btn-icon x-edit-forecolor',
23112                     clickEvent:'mousedown',
23113                     tooltip: this.buttonTips['forecolor'] || undefined,
23114                     tabIndex:-1,
23115                     menu : new Roo.menu.ColorMenu({
23116                         allowReselect: true,
23117                         focus: Roo.emptyFn,
23118                         value:'000000',
23119                         plain:true,
23120                         selectHandler: function(cp, color){
23121                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23122                             editor.deferFocus();
23123                         },
23124                         scope: editorcore,
23125                         clickEvent:'mousedown'
23126                     })
23127                 }, {
23128                     id:editorcore.frameId +'backcolor',
23129                     cls:'x-btn-icon x-edit-backcolor',
23130                     clickEvent:'mousedown',
23131                     tooltip: this.buttonTips['backcolor'] || undefined,
23132                     tabIndex:-1,
23133                     menu : new Roo.menu.ColorMenu({
23134                         focus: Roo.emptyFn,
23135                         value:'FFFFFF',
23136                         plain:true,
23137                         allowReselect: true,
23138                         selectHandler: function(cp, color){
23139                             if(Roo.isGecko){
23140                                 editorcore.execCmd('useCSS', false);
23141                                 editorcore.execCmd('hilitecolor', color);
23142                                 editorcore.execCmd('useCSS', true);
23143                                 editor.deferFocus();
23144                             }else{
23145                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23146                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23147                                 editor.deferFocus();
23148                             }
23149                         },
23150                         scope:editorcore,
23151                         clickEvent:'mousedown'
23152                     })
23153                 }
23154             );
23155         };
23156         // now add all the items...
23157         
23158
23159         if(!this.disable.alignments){
23160             tb.add(
23161                 '-',
23162                 btn('justifyleft'),
23163                 btn('justifycenter'),
23164                 btn('justifyright')
23165             );
23166         };
23167
23168         //if(!Roo.isSafari){
23169             if(!this.disable.links){
23170                 tb.add(
23171                     '-',
23172                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23173                 );
23174             };
23175
23176             if(!this.disable.lists){
23177                 tb.add(
23178                     '-',
23179                     btn('insertorderedlist'),
23180                     btn('insertunorderedlist')
23181                 );
23182             }
23183             if(!this.disable.sourceEdit){
23184                 tb.add(
23185                     '-',
23186                     btn('sourceedit', true, function(btn){
23187                         this.toggleSourceEdit(btn.pressed);
23188                     })
23189                 );
23190             }
23191         //}
23192         
23193         var smenu = { };
23194         // special menu.. - needs to be tidied up..
23195         if (!this.disable.special) {
23196             smenu = {
23197                 text: "&#169;",
23198                 cls: 'x-edit-none',
23199                 
23200                 menu : {
23201                     items : []
23202                 }
23203             };
23204             for (var i =0; i < this.specialChars.length; i++) {
23205                 smenu.menu.items.push({
23206                     
23207                     html: this.specialChars[i],
23208                     handler: function(a,b) {
23209                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23210                         //editor.insertAtCursor(a.html);
23211                         
23212                     },
23213                     tabIndex:-1
23214                 });
23215             }
23216             
23217             
23218             tb.add(smenu);
23219             
23220             
23221         }
23222         
23223         var cmenu = { };
23224         if (!this.disable.cleanStyles) {
23225             cmenu = {
23226                 cls: 'x-btn-icon x-btn-clear',
23227                 
23228                 menu : {
23229                     items : []
23230                 }
23231             };
23232             for (var i =0; i < this.cleanStyles.length; i++) {
23233                 cmenu.menu.items.push({
23234                     actiontype : this.cleanStyles[i],
23235                     html: 'Remove ' + this.cleanStyles[i],
23236                     handler: function(a,b) {
23237 //                        Roo.log(a);
23238 //                        Roo.log(b);
23239                         var c = Roo.get(editorcore.doc.body);
23240                         c.select('[style]').each(function(s) {
23241                             s.dom.style.removeProperty(a.actiontype);
23242                         });
23243                         editorcore.syncValue();
23244                     },
23245                     tabIndex:-1
23246                 });
23247             }
23248              cmenu.menu.items.push({
23249                 actiontype : 'tablewidths',
23250                 html: 'Remove Table Widths',
23251                 handler: function(a,b) {
23252                     editorcore.cleanTableWidths();
23253                     editorcore.syncValue();
23254                 },
23255                 tabIndex:-1
23256             });
23257             cmenu.menu.items.push({
23258                 actiontype : 'word',
23259                 html: 'Remove MS Word Formating',
23260                 handler: function(a,b) {
23261                     editorcore.cleanWord();
23262                     editorcore.syncValue();
23263                 },
23264                 tabIndex:-1
23265             });
23266             
23267             cmenu.menu.items.push({
23268                 actiontype : 'all',
23269                 html: 'Remove All Styles',
23270                 handler: function(a,b) {
23271                     
23272                     var c = Roo.get(editorcore.doc.body);
23273                     c.select('[style]').each(function(s) {
23274                         s.dom.removeAttribute('style');
23275                     });
23276                     editorcore.syncValue();
23277                 },
23278                 tabIndex:-1
23279             });
23280             
23281             cmenu.menu.items.push({
23282                 actiontype : 'all',
23283                 html: 'Remove All CSS Classes',
23284                 handler: function(a,b) {
23285                     
23286                     var c = Roo.get(editorcore.doc.body);
23287                     c.select('[class]').each(function(s) {
23288                         s.dom.removeAttribute('class');
23289                     });
23290                     editorcore.cleanWord();
23291                     editorcore.syncValue();
23292                 },
23293                 tabIndex:-1
23294             });
23295             
23296              cmenu.menu.items.push({
23297                 actiontype : 'tidy',
23298                 html: 'Tidy HTML Source',
23299                 handler: function(a,b) {
23300                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23301                     editorcore.syncValue();
23302                 },
23303                 tabIndex:-1
23304             });
23305             
23306             
23307             tb.add(cmenu);
23308         }
23309          
23310         if (!this.disable.specialElements) {
23311             var semenu = {
23312                 text: "Other;",
23313                 cls: 'x-edit-none',
23314                 menu : {
23315                     items : []
23316                 }
23317             };
23318             for (var i =0; i < this.specialElements.length; i++) {
23319                 semenu.menu.items.push(
23320                     Roo.apply({ 
23321                         handler: function(a,b) {
23322                             editor.insertAtCursor(this.ihtml);
23323                         }
23324                     }, this.specialElements[i])
23325                 );
23326                     
23327             }
23328             
23329             tb.add(semenu);
23330             
23331             
23332         }
23333          
23334         
23335         if (this.btns) {
23336             for(var i =0; i< this.btns.length;i++) {
23337                 var b = Roo.factory(this.btns[i],Roo.form);
23338                 b.cls =  'x-edit-none';
23339                 
23340                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23341                     b.cls += ' x-init-enable';
23342                 }
23343                 
23344                 b.scope = editorcore;
23345                 tb.add(b);
23346             }
23347         
23348         }
23349         
23350         
23351         
23352         // disable everything...
23353         
23354         this.tb.items.each(function(item){
23355             
23356            if(
23357                 item.id != editorcore.frameId+ '-sourceedit' && 
23358                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23359             ){
23360                 
23361                 item.disable();
23362             }
23363         });
23364         this.rendered = true;
23365         
23366         // the all the btns;
23367         editor.on('editorevent', this.updateToolbar, this);
23368         // other toolbars need to implement this..
23369         //editor.on('editmodechange', this.updateToolbar, this);
23370     },
23371     
23372     
23373     relayBtnCmd : function(btn) {
23374         this.editorcore.relayCmd(btn.cmd);
23375     },
23376     // private used internally
23377     createLink : function(){
23378         Roo.log("create link?");
23379         var url = prompt(this.createLinkText, this.defaultLinkValue);
23380         if(url && url != 'http:/'+'/'){
23381             this.editorcore.relayCmd('createlink', url);
23382         }
23383     },
23384
23385     
23386     /**
23387      * Protected method that will not generally be called directly. It triggers
23388      * a toolbar update by reading the markup state of the current selection in the editor.
23389      */
23390     updateToolbar: function(){
23391
23392         if(!this.editorcore.activated){
23393             this.editor.onFirstFocus();
23394             return;
23395         }
23396
23397         var btns = this.tb.items.map, 
23398             doc = this.editorcore.doc,
23399             frameId = this.editorcore.frameId;
23400
23401         if(!this.disable.font && !Roo.isSafari){
23402             /*
23403             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23404             if(name != this.fontSelect.dom.value){
23405                 this.fontSelect.dom.value = name;
23406             }
23407             */
23408         }
23409         if(!this.disable.format){
23410             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23411             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23412             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23413             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23414         }
23415         if(!this.disable.alignments){
23416             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23417             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23418             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23419         }
23420         if(!Roo.isSafari && !this.disable.lists){
23421             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23422             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23423         }
23424         
23425         var ans = this.editorcore.getAllAncestors();
23426         if (this.formatCombo) {
23427             
23428             
23429             var store = this.formatCombo.store;
23430             this.formatCombo.setValue("");
23431             for (var i =0; i < ans.length;i++) {
23432                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23433                     // select it..
23434                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23435                     break;
23436                 }
23437             }
23438         }
23439         
23440         
23441         
23442         // hides menus... - so this cant be on a menu...
23443         Roo.menu.MenuMgr.hideAll();
23444
23445         //this.editorsyncValue();
23446     },
23447    
23448     
23449     createFontOptions : function(){
23450         var buf = [], fs = this.fontFamilies, ff, lc;
23451         
23452         
23453         
23454         for(var i = 0, len = fs.length; i< len; i++){
23455             ff = fs[i];
23456             lc = ff.toLowerCase();
23457             buf.push(
23458                 '<option value="',lc,'" style="font-family:',ff,';"',
23459                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23460                     ff,
23461                 '</option>'
23462             );
23463         }
23464         return buf.join('');
23465     },
23466     
23467     toggleSourceEdit : function(sourceEditMode){
23468         
23469         Roo.log("toolbar toogle");
23470         if(sourceEditMode === undefined){
23471             sourceEditMode = !this.sourceEditMode;
23472         }
23473         this.sourceEditMode = sourceEditMode === true;
23474         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23475         // just toggle the button?
23476         if(btn.pressed !== this.sourceEditMode){
23477             btn.toggle(this.sourceEditMode);
23478             return;
23479         }
23480         
23481         if(sourceEditMode){
23482             Roo.log("disabling buttons");
23483             this.tb.items.each(function(item){
23484                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23485                     item.disable();
23486                 }
23487             });
23488           
23489         }else{
23490             Roo.log("enabling buttons");
23491             if(this.editorcore.initialized){
23492                 this.tb.items.each(function(item){
23493                     item.enable();
23494                 });
23495             }
23496             
23497         }
23498         Roo.log("calling toggole on editor");
23499         // tell the editor that it's been pressed..
23500         this.editor.toggleSourceEdit(sourceEditMode);
23501        
23502     },
23503      /**
23504      * Object collection of toolbar tooltips for the buttons in the editor. The key
23505      * is the command id associated with that button and the value is a valid QuickTips object.
23506      * For example:
23507 <pre><code>
23508 {
23509     bold : {
23510         title: 'Bold (Ctrl+B)',
23511         text: 'Make the selected text bold.',
23512         cls: 'x-html-editor-tip'
23513     },
23514     italic : {
23515         title: 'Italic (Ctrl+I)',
23516         text: 'Make the selected text italic.',
23517         cls: 'x-html-editor-tip'
23518     },
23519     ...
23520 </code></pre>
23521     * @type Object
23522      */
23523     buttonTips : {
23524         bold : {
23525             title: 'Bold (Ctrl+B)',
23526             text: 'Make the selected text bold.',
23527             cls: 'x-html-editor-tip'
23528         },
23529         italic : {
23530             title: 'Italic (Ctrl+I)',
23531             text: 'Make the selected text italic.',
23532             cls: 'x-html-editor-tip'
23533         },
23534         underline : {
23535             title: 'Underline (Ctrl+U)',
23536             text: 'Underline the selected text.',
23537             cls: 'x-html-editor-tip'
23538         },
23539         strikethrough : {
23540             title: 'Strikethrough',
23541             text: 'Strikethrough the selected text.',
23542             cls: 'x-html-editor-tip'
23543         },
23544         increasefontsize : {
23545             title: 'Grow Text',
23546             text: 'Increase the font size.',
23547             cls: 'x-html-editor-tip'
23548         },
23549         decreasefontsize : {
23550             title: 'Shrink Text',
23551             text: 'Decrease the font size.',
23552             cls: 'x-html-editor-tip'
23553         },
23554         backcolor : {
23555             title: 'Text Highlight Color',
23556             text: 'Change the background color of the selected text.',
23557             cls: 'x-html-editor-tip'
23558         },
23559         forecolor : {
23560             title: 'Font Color',
23561             text: 'Change the color of the selected text.',
23562             cls: 'x-html-editor-tip'
23563         },
23564         justifyleft : {
23565             title: 'Align Text Left',
23566             text: 'Align text to the left.',
23567             cls: 'x-html-editor-tip'
23568         },
23569         justifycenter : {
23570             title: 'Center Text',
23571             text: 'Center text in the editor.',
23572             cls: 'x-html-editor-tip'
23573         },
23574         justifyright : {
23575             title: 'Align Text Right',
23576             text: 'Align text to the right.',
23577             cls: 'x-html-editor-tip'
23578         },
23579         insertunorderedlist : {
23580             title: 'Bullet List',
23581             text: 'Start a bulleted list.',
23582             cls: 'x-html-editor-tip'
23583         },
23584         insertorderedlist : {
23585             title: 'Numbered List',
23586             text: 'Start a numbered list.',
23587             cls: 'x-html-editor-tip'
23588         },
23589         createlink : {
23590             title: 'Hyperlink',
23591             text: 'Make the selected text a hyperlink.',
23592             cls: 'x-html-editor-tip'
23593         },
23594         sourceedit : {
23595             title: 'Source Edit',
23596             text: 'Switch to source editing mode.',
23597             cls: 'x-html-editor-tip'
23598         }
23599     },
23600     // private
23601     onDestroy : function(){
23602         if(this.rendered){
23603             
23604             this.tb.items.each(function(item){
23605                 if(item.menu){
23606                     item.menu.removeAll();
23607                     if(item.menu.el){
23608                         item.menu.el.destroy();
23609                     }
23610                 }
23611                 item.destroy();
23612             });
23613              
23614         }
23615     },
23616     onFirstFocus: function() {
23617         this.tb.items.each(function(item){
23618            item.enable();
23619         });
23620     }
23621 });
23622
23623
23624
23625
23626 // <script type="text/javascript">
23627 /*
23628  * Based on
23629  * Ext JS Library 1.1.1
23630  * Copyright(c) 2006-2007, Ext JS, LLC.
23631  *  
23632  
23633  */
23634
23635  
23636 /**
23637  * @class Roo.form.HtmlEditor.ToolbarContext
23638  * Context Toolbar
23639  * 
23640  * Usage:
23641  *
23642  new Roo.form.HtmlEditor({
23643     ....
23644     toolbars : [
23645         { xtype: 'ToolbarStandard', styles : {} }
23646         { xtype: 'ToolbarContext', disable : {} }
23647     ]
23648 })
23649
23650      
23651  * 
23652  * @config : {Object} disable List of elements to disable.. (not done yet.)
23653  * @config : {Object} styles  Map of styles available.
23654  * 
23655  */
23656
23657 Roo.form.HtmlEditor.ToolbarContext = function(config)
23658 {
23659     
23660     Roo.apply(this, config);
23661     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23662     // dont call parent... till later.
23663     this.styles = this.styles || {};
23664 }
23665
23666  
23667
23668 Roo.form.HtmlEditor.ToolbarContext.types = {
23669     'IMG' : {
23670         width : {
23671             title: "Width",
23672             width: 40
23673         },
23674         height:  {
23675             title: "Height",
23676             width: 40
23677         },
23678         align: {
23679             title: "Align",
23680             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23681             width : 80
23682             
23683         },
23684         border: {
23685             title: "Border",
23686             width: 40
23687         },
23688         alt: {
23689             title: "Alt",
23690             width: 120
23691         },
23692         src : {
23693             title: "Src",
23694             width: 220
23695         }
23696         
23697     },
23698     'A' : {
23699         name : {
23700             title: "Name",
23701             width: 50
23702         },
23703         target:  {
23704             title: "Target",
23705             width: 120
23706         },
23707         href:  {
23708             title: "Href",
23709             width: 220
23710         } // border?
23711         
23712     },
23713     'TABLE' : {
23714         rows : {
23715             title: "Rows",
23716             width: 20
23717         },
23718         cols : {
23719             title: "Cols",
23720             width: 20
23721         },
23722         width : {
23723             title: "Width",
23724             width: 40
23725         },
23726         height : {
23727             title: "Height",
23728             width: 40
23729         },
23730         border : {
23731             title: "Border",
23732             width: 20
23733         }
23734     },
23735     'TD' : {
23736         width : {
23737             title: "Width",
23738             width: 40
23739         },
23740         height : {
23741             title: "Height",
23742             width: 40
23743         },   
23744         align: {
23745             title: "Align",
23746             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23747             width: 80
23748         },
23749         valign: {
23750             title: "Valign",
23751             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23752             width: 80
23753         },
23754         colspan: {
23755             title: "Colspan",
23756             width: 20
23757             
23758         },
23759          'font-family'  : {
23760             title : "Font",
23761             style : 'fontFamily',
23762             displayField: 'display',
23763             optname : 'font-family',
23764             width: 140
23765         }
23766     },
23767     'INPUT' : {
23768         name : {
23769             title: "name",
23770             width: 120
23771         },
23772         value : {
23773             title: "Value",
23774             width: 120
23775         },
23776         width : {
23777             title: "Width",
23778             width: 40
23779         }
23780     },
23781     'LABEL' : {
23782         'for' : {
23783             title: "For",
23784             width: 120
23785         }
23786     },
23787     'TEXTAREA' : {
23788           name : {
23789             title: "name",
23790             width: 120
23791         },
23792         rows : {
23793             title: "Rows",
23794             width: 20
23795         },
23796         cols : {
23797             title: "Cols",
23798             width: 20
23799         }
23800     },
23801     'SELECT' : {
23802         name : {
23803             title: "name",
23804             width: 120
23805         },
23806         selectoptions : {
23807             title: "Options",
23808             width: 200
23809         }
23810     },
23811     
23812     // should we really allow this??
23813     // should this just be 
23814     'BODY' : {
23815         title : {
23816             title: "Title",
23817             width: 200,
23818             disabled : true
23819         }
23820     },
23821     'SPAN' : {
23822         'font-family'  : {
23823             title : "Font",
23824             style : 'fontFamily',
23825             displayField: 'display',
23826             optname : 'font-family',
23827             width: 140
23828         }
23829     },
23830     'DIV' : {
23831         'font-family'  : {
23832             title : "Font",
23833             style : 'fontFamily',
23834             displayField: 'display',
23835             optname : 'font-family',
23836             width: 140
23837         }
23838     },
23839      'P' : {
23840         'font-family'  : {
23841             title : "Font",
23842             style : 'fontFamily',
23843             displayField: 'display',
23844             optname : 'font-family',
23845             width: 140
23846         }
23847     },
23848     
23849     '*' : {
23850         // empty..
23851     }
23852
23853 };
23854
23855 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23856 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23857
23858 Roo.form.HtmlEditor.ToolbarContext.options = {
23859         'font-family'  : [ 
23860                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23861                 [ 'Courier New', 'Courier New'],
23862                 [ 'Tahoma', 'Tahoma'],
23863                 [ 'Times New Roman,serif', 'Times'],
23864                 [ 'Verdana','Verdana' ]
23865         ]
23866 };
23867
23868 // fixme - these need to be configurable..
23869  
23870
23871 //Roo.form.HtmlEditor.ToolbarContext.types
23872
23873
23874 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23875     
23876     tb: false,
23877     
23878     rendered: false,
23879     
23880     editor : false,
23881     editorcore : false,
23882     /**
23883      * @cfg {Object} disable  List of toolbar elements to disable
23884          
23885      */
23886     disable : false,
23887     /**
23888      * @cfg {Object} styles List of styles 
23889      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23890      *
23891      * These must be defined in the page, so they get rendered correctly..
23892      * .headline { }
23893      * TD.underline { }
23894      * 
23895      */
23896     styles : false,
23897     
23898     options: false,
23899     
23900     toolbars : false,
23901     
23902     init : function(editor)
23903     {
23904         this.editor = editor;
23905         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23906         var editorcore = this.editorcore;
23907         
23908         var fid = editorcore.frameId;
23909         var etb = this;
23910         function btn(id, toggle, handler){
23911             var xid = fid + '-'+ id ;
23912             return {
23913                 id : xid,
23914                 cmd : id,
23915                 cls : 'x-btn-icon x-edit-'+id,
23916                 enableToggle:toggle !== false,
23917                 scope: editorcore, // was editor...
23918                 handler:handler||editorcore.relayBtnCmd,
23919                 clickEvent:'mousedown',
23920                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23921                 tabIndex:-1
23922             };
23923         }
23924         // create a new element.
23925         var wdiv = editor.wrap.createChild({
23926                 tag: 'div'
23927             }, editor.wrap.dom.firstChild.nextSibling, true);
23928         
23929         // can we do this more than once??
23930         
23931          // stop form submits
23932       
23933  
23934         // disable everything...
23935         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23936         this.toolbars = {};
23937            
23938         for (var i in  ty) {
23939           
23940             this.toolbars[i] = this.buildToolbar(ty[i],i);
23941         }
23942         this.tb = this.toolbars.BODY;
23943         this.tb.el.show();
23944         this.buildFooter();
23945         this.footer.show();
23946         editor.on('hide', function( ) { this.footer.hide() }, this);
23947         editor.on('show', function( ) { this.footer.show() }, this);
23948         
23949          
23950         this.rendered = true;
23951         
23952         // the all the btns;
23953         editor.on('editorevent', this.updateToolbar, this);
23954         // other toolbars need to implement this..
23955         //editor.on('editmodechange', this.updateToolbar, this);
23956     },
23957     
23958     
23959     
23960     /**
23961      * Protected method that will not generally be called directly. It triggers
23962      * a toolbar update by reading the markup state of the current selection in the editor.
23963      *
23964      * Note you can force an update by calling on('editorevent', scope, false)
23965      */
23966     updateToolbar: function(editor,ev,sel){
23967
23968         //Roo.log(ev);
23969         // capture mouse up - this is handy for selecting images..
23970         // perhaps should go somewhere else...
23971         if(!this.editorcore.activated){
23972              this.editor.onFirstFocus();
23973             return;
23974         }
23975         
23976         
23977         
23978         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23979         // selectNode - might want to handle IE?
23980         if (ev &&
23981             (ev.type == 'mouseup' || ev.type == 'click' ) &&
23982             ev.target && ev.target.tagName == 'IMG') {
23983             // they have click on an image...
23984             // let's see if we can change the selection...
23985             sel = ev.target;
23986          
23987               var nodeRange = sel.ownerDocument.createRange();
23988             try {
23989                 nodeRange.selectNode(sel);
23990             } catch (e) {
23991                 nodeRange.selectNodeContents(sel);
23992             }
23993             //nodeRange.collapse(true);
23994             var s = this.editorcore.win.getSelection();
23995             s.removeAllRanges();
23996             s.addRange(nodeRange);
23997         }  
23998         
23999       
24000         var updateFooter = sel ? false : true;
24001         
24002         
24003         var ans = this.editorcore.getAllAncestors();
24004         
24005         // pick
24006         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24007         
24008         if (!sel) { 
24009             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
24010             sel = sel ? sel : this.editorcore.doc.body;
24011             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24012             
24013         }
24014         // pick a menu that exists..
24015         var tn = sel.tagName.toUpperCase();
24016         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24017         
24018         tn = sel.tagName.toUpperCase();
24019         
24020         var lastSel = this.tb.selectedNode;
24021         
24022         this.tb.selectedNode = sel;
24023         
24024         // if current menu does not match..
24025         
24026         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24027                 
24028             this.tb.el.hide();
24029             ///console.log("show: " + tn);
24030             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24031             this.tb.el.show();
24032             // update name
24033             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
24034             
24035             
24036             // update attributes
24037             if (this.tb.fields) {
24038                 this.tb.fields.each(function(e) {
24039                     if (e.stylename) {
24040                         e.setValue(sel.style[e.stylename]);
24041                         return;
24042                     } 
24043                    e.setValue(sel.getAttribute(e.attrname));
24044                 });
24045             }
24046             
24047             var hasStyles = false;
24048             for(var i in this.styles) {
24049                 hasStyles = true;
24050                 break;
24051             }
24052             
24053             // update styles
24054             if (hasStyles) { 
24055                 var st = this.tb.fields.item(0);
24056                 
24057                 st.store.removeAll();
24058                
24059                 
24060                 var cn = sel.className.split(/\s+/);
24061                 
24062                 var avs = [];
24063                 if (this.styles['*']) {
24064                     
24065                     Roo.each(this.styles['*'], function(v) {
24066                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24067                     });
24068                 }
24069                 if (this.styles[tn]) { 
24070                     Roo.each(this.styles[tn], function(v) {
24071                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24072                     });
24073                 }
24074                 
24075                 st.store.loadData(avs);
24076                 st.collapse();
24077                 st.setValue(cn);
24078             }
24079             // flag our selected Node.
24080             this.tb.selectedNode = sel;
24081            
24082            
24083             Roo.menu.MenuMgr.hideAll();
24084
24085         }
24086         
24087         if (!updateFooter) {
24088             //this.footDisp.dom.innerHTML = ''; 
24089             return;
24090         }
24091         // update the footer
24092         //
24093         var html = '';
24094         
24095         this.footerEls = ans.reverse();
24096         Roo.each(this.footerEls, function(a,i) {
24097             if (!a) { return; }
24098             html += html.length ? ' &gt; '  :  '';
24099             
24100             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24101             
24102         });
24103        
24104         // 
24105         var sz = this.footDisp.up('td').getSize();
24106         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24107         this.footDisp.dom.style.marginLeft = '5px';
24108         
24109         this.footDisp.dom.style.overflow = 'hidden';
24110         
24111         this.footDisp.dom.innerHTML = html;
24112             
24113         //this.editorsyncValue();
24114     },
24115      
24116     
24117    
24118        
24119     // private
24120     onDestroy : function(){
24121         if(this.rendered){
24122             
24123             this.tb.items.each(function(item){
24124                 if(item.menu){
24125                     item.menu.removeAll();
24126                     if(item.menu.el){
24127                         item.menu.el.destroy();
24128                     }
24129                 }
24130                 item.destroy();
24131             });
24132              
24133         }
24134     },
24135     onFirstFocus: function() {
24136         // need to do this for all the toolbars..
24137         this.tb.items.each(function(item){
24138            item.enable();
24139         });
24140     },
24141     buildToolbar: function(tlist, nm)
24142     {
24143         var editor = this.editor;
24144         var editorcore = this.editorcore;
24145          // create a new element.
24146         var wdiv = editor.wrap.createChild({
24147                 tag: 'div'
24148             }, editor.wrap.dom.firstChild.nextSibling, true);
24149         
24150        
24151         var tb = new Roo.Toolbar(wdiv);
24152         // add the name..
24153         
24154         tb.add(nm+ ":&nbsp;");
24155         
24156         var styles = [];
24157         for(var i in this.styles) {
24158             styles.push(i);
24159         }
24160         
24161         // styles...
24162         if (styles && styles.length) {
24163             
24164             // this needs a multi-select checkbox...
24165             tb.addField( new Roo.form.ComboBox({
24166                 store: new Roo.data.SimpleStore({
24167                     id : 'val',
24168                     fields: ['val', 'selected'],
24169                     data : [] 
24170                 }),
24171                 name : '-roo-edit-className',
24172                 attrname : 'className',
24173                 displayField: 'val',
24174                 typeAhead: false,
24175                 mode: 'local',
24176                 editable : false,
24177                 triggerAction: 'all',
24178                 emptyText:'Select Style',
24179                 selectOnFocus:true,
24180                 width: 130,
24181                 listeners : {
24182                     'select': function(c, r, i) {
24183                         // initial support only for on class per el..
24184                         tb.selectedNode.className =  r ? r.get('val') : '';
24185                         editorcore.syncValue();
24186                     }
24187                 }
24188     
24189             }));
24190         }
24191         
24192         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24193         var tbops = tbc.options;
24194         
24195         for (var i in tlist) {
24196             
24197             var item = tlist[i];
24198             tb.add(item.title + ":&nbsp;");
24199             
24200             
24201             //optname == used so you can configure the options available..
24202             var opts = item.opts ? item.opts : false;
24203             if (item.optname) {
24204                 opts = tbops[item.optname];
24205            
24206             }
24207             
24208             if (opts) {
24209                 // opts == pulldown..
24210                 tb.addField( new Roo.form.ComboBox({
24211                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24212                         id : 'val',
24213                         fields: ['val', 'display'],
24214                         data : opts  
24215                     }),
24216                     name : '-roo-edit-' + i,
24217                     attrname : i,
24218                     stylename : item.style ? item.style : false,
24219                     displayField: item.displayField ? item.displayField : 'val',
24220                     valueField :  'val',
24221                     typeAhead: false,
24222                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24223                     editable : false,
24224                     triggerAction: 'all',
24225                     emptyText:'Select',
24226                     selectOnFocus:true,
24227                     width: item.width ? item.width  : 130,
24228                     listeners : {
24229                         'select': function(c, r, i) {
24230                             if (c.stylename) {
24231                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24232                                 return;
24233                             }
24234                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24235                         }
24236                     }
24237
24238                 }));
24239                 continue;
24240                     
24241                  
24242                 
24243                 tb.addField( new Roo.form.TextField({
24244                     name: i,
24245                     width: 100,
24246                     //allowBlank:false,
24247                     value: ''
24248                 }));
24249                 continue;
24250             }
24251             tb.addField( new Roo.form.TextField({
24252                 name: '-roo-edit-' + i,
24253                 attrname : i,
24254                 
24255                 width: item.width,
24256                 //allowBlank:true,
24257                 value: '',
24258                 listeners: {
24259                     'change' : function(f, nv, ov) {
24260                         tb.selectedNode.setAttribute(f.attrname, nv);
24261                         editorcore.syncValue();
24262                     }
24263                 }
24264             }));
24265              
24266         }
24267         
24268         var _this = this;
24269         
24270         if(nm == 'BODY'){
24271             tb.addSeparator();
24272         
24273             tb.addButton( {
24274                 text: 'Stylesheets',
24275
24276                 listeners : {
24277                     click : function ()
24278                     {
24279                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24280                     }
24281                 }
24282             });
24283         }
24284         
24285         tb.addFill();
24286         tb.addButton( {
24287             text: 'Remove Tag',
24288     
24289             listeners : {
24290                 click : function ()
24291                 {
24292                     // remove
24293                     // undo does not work.
24294                      
24295                     var sn = tb.selectedNode;
24296                     
24297                     var pn = sn.parentNode;
24298                     
24299                     var stn =  sn.childNodes[0];
24300                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24301                     while (sn.childNodes.length) {
24302                         var node = sn.childNodes[0];
24303                         sn.removeChild(node);
24304                         //Roo.log(node);
24305                         pn.insertBefore(node, sn);
24306                         
24307                     }
24308                     pn.removeChild(sn);
24309                     var range = editorcore.createRange();
24310         
24311                     range.setStart(stn,0);
24312                     range.setEnd(en,0); //????
24313                     //range.selectNode(sel);
24314                     
24315                     
24316                     var selection = editorcore.getSelection();
24317                     selection.removeAllRanges();
24318                     selection.addRange(range);
24319                     
24320                     
24321                     
24322                     //_this.updateToolbar(null, null, pn);
24323                     _this.updateToolbar(null, null, null);
24324                     _this.footDisp.dom.innerHTML = ''; 
24325                 }
24326             }
24327             
24328                     
24329                 
24330             
24331         });
24332         
24333         
24334         tb.el.on('click', function(e){
24335             e.preventDefault(); // what does this do?
24336         });
24337         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24338         tb.el.hide();
24339         tb.name = nm;
24340         // dont need to disable them... as they will get hidden
24341         return tb;
24342          
24343         
24344     },
24345     buildFooter : function()
24346     {
24347         
24348         var fel = this.editor.wrap.createChild();
24349         this.footer = new Roo.Toolbar(fel);
24350         // toolbar has scrolly on left / right?
24351         var footDisp= new Roo.Toolbar.Fill();
24352         var _t = this;
24353         this.footer.add(
24354             {
24355                 text : '&lt;',
24356                 xtype: 'Button',
24357                 handler : function() {
24358                     _t.footDisp.scrollTo('left',0,true)
24359                 }
24360             }
24361         );
24362         this.footer.add( footDisp );
24363         this.footer.add( 
24364             {
24365                 text : '&gt;',
24366                 xtype: 'Button',
24367                 handler : function() {
24368                     // no animation..
24369                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24370                 }
24371             }
24372         );
24373         var fel = Roo.get(footDisp.el);
24374         fel.addClass('x-editor-context');
24375         this.footDispWrap = fel; 
24376         this.footDispWrap.overflow  = 'hidden';
24377         
24378         this.footDisp = fel.createChild();
24379         this.footDispWrap.on('click', this.onContextClick, this)
24380         
24381         
24382     },
24383     onContextClick : function (ev,dom)
24384     {
24385         ev.preventDefault();
24386         var  cn = dom.className;
24387         //Roo.log(cn);
24388         if (!cn.match(/x-ed-loc-/)) {
24389             return;
24390         }
24391         var n = cn.split('-').pop();
24392         var ans = this.footerEls;
24393         var sel = ans[n];
24394         
24395          // pick
24396         var range = this.editorcore.createRange();
24397         
24398         range.selectNodeContents(sel);
24399         //range.selectNode(sel);
24400         
24401         
24402         var selection = this.editorcore.getSelection();
24403         selection.removeAllRanges();
24404         selection.addRange(range);
24405         
24406         
24407         
24408         this.updateToolbar(null, null, sel);
24409         
24410         
24411     }
24412     
24413     
24414     
24415     
24416     
24417 });
24418
24419
24420
24421
24422
24423 /*
24424  * Based on:
24425  * Ext JS Library 1.1.1
24426  * Copyright(c) 2006-2007, Ext JS, LLC.
24427  *
24428  * Originally Released Under LGPL - original licence link has changed is not relivant.
24429  *
24430  * Fork - LGPL
24431  * <script type="text/javascript">
24432  */
24433  
24434 /**
24435  * @class Roo.form.BasicForm
24436  * @extends Roo.util.Observable
24437  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24438  * @constructor
24439  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24440  * @param {Object} config Configuration options
24441  */
24442 Roo.form.BasicForm = function(el, config){
24443     this.allItems = [];
24444     this.childForms = [];
24445     Roo.apply(this, config);
24446     /*
24447      * The Roo.form.Field items in this form.
24448      * @type MixedCollection
24449      */
24450      
24451      
24452     this.items = new Roo.util.MixedCollection(false, function(o){
24453         return o.id || (o.id = Roo.id());
24454     });
24455     this.addEvents({
24456         /**
24457          * @event beforeaction
24458          * Fires before any action is performed. Return false to cancel the action.
24459          * @param {Form} this
24460          * @param {Action} action The action to be performed
24461          */
24462         beforeaction: true,
24463         /**
24464          * @event actionfailed
24465          * Fires when an action fails.
24466          * @param {Form} this
24467          * @param {Action} action The action that failed
24468          */
24469         actionfailed : true,
24470         /**
24471          * @event actioncomplete
24472          * Fires when an action is completed.
24473          * @param {Form} this
24474          * @param {Action} action The action that completed
24475          */
24476         actioncomplete : true
24477     });
24478     if(el){
24479         this.initEl(el);
24480     }
24481     Roo.form.BasicForm.superclass.constructor.call(this);
24482     
24483     Roo.form.BasicForm.popover.apply();
24484 };
24485
24486 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24487     /**
24488      * @cfg {String} method
24489      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24490      */
24491     /**
24492      * @cfg {DataReader} reader
24493      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24494      * This is optional as there is built-in support for processing JSON.
24495      */
24496     /**
24497      * @cfg {DataReader} errorReader
24498      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24499      * This is completely optional as there is built-in support for processing JSON.
24500      */
24501     /**
24502      * @cfg {String} url
24503      * The URL to use for form actions if one isn't supplied in the action options.
24504      */
24505     /**
24506      * @cfg {Boolean} fileUpload
24507      * Set to true if this form is a file upload.
24508      */
24509      
24510     /**
24511      * @cfg {Object} baseParams
24512      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24513      */
24514      /**
24515      
24516     /**
24517      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24518      */
24519     timeout: 30,
24520
24521     // private
24522     activeAction : null,
24523
24524     /**
24525      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24526      * or setValues() data instead of when the form was first created.
24527      */
24528     trackResetOnLoad : false,
24529     
24530     
24531     /**
24532      * childForms - used for multi-tab forms
24533      * @type {Array}
24534      */
24535     childForms : false,
24536     
24537     /**
24538      * allItems - full list of fields.
24539      * @type {Array}
24540      */
24541     allItems : false,
24542     
24543     /**
24544      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24545      * element by passing it or its id or mask the form itself by passing in true.
24546      * @type Mixed
24547      */
24548     waitMsgTarget : false,
24549     
24550     /**
24551      * @type Boolean
24552      */
24553     disableMask : false,
24554     
24555     /**
24556      * @cfg {Boolean} errorMask (true|false) default false
24557      */
24558     errorMask : false,
24559     
24560     /**
24561      * @cfg {Number} maskOffset Default 100
24562      */
24563     maskOffset : 100,
24564
24565     // private
24566     initEl : function(el){
24567         this.el = Roo.get(el);
24568         this.id = this.el.id || Roo.id();
24569         this.el.on('submit', this.onSubmit, this);
24570         this.el.addClass('x-form');
24571     },
24572
24573     // private
24574     onSubmit : function(e){
24575         e.stopEvent();
24576     },
24577
24578     /**
24579      * Returns true if client-side validation on the form is successful.
24580      * @return Boolean
24581      */
24582     isValid : function(){
24583         var valid = true;
24584         var target = false;
24585         this.items.each(function(f){
24586             if(f.validate()){
24587                 return;
24588             }
24589             
24590             valid = false;
24591                 
24592             if(!target && f.el.isVisible(true)){
24593                 target = f;
24594             }
24595         });
24596         
24597         if(this.errorMask && !valid){
24598             Roo.form.BasicForm.popover.mask(this, target);
24599         }
24600         
24601         return valid;
24602     },
24603     /**
24604      * Returns array of invalid form fields.
24605      * @return Array
24606      */
24607     
24608     invalidFields : function()
24609     {
24610         var ret = [];
24611         this.items.each(function(f){
24612             if(f.validate()){
24613                 return;
24614             }
24615             ret.push(f);
24616             
24617         });
24618         
24619         return ret;
24620     },
24621     
24622     
24623     /**
24624      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24625      * @return Boolean
24626      */
24627     isDirty : function(){
24628         var dirty = false;
24629         this.items.each(function(f){
24630            if(f.isDirty()){
24631                dirty = true;
24632                return false;
24633            }
24634         });
24635         return dirty;
24636     },
24637     
24638     /**
24639      * Returns true if any fields in this form have changed since their original load. (New version)
24640      * @return Boolean
24641      */
24642     
24643     hasChanged : function()
24644     {
24645         var dirty = false;
24646         this.items.each(function(f){
24647            if(f.hasChanged()){
24648                dirty = true;
24649                return false;
24650            }
24651         });
24652         return dirty;
24653         
24654     },
24655     /**
24656      * Resets all hasChanged to 'false' -
24657      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24658      * So hasChanged storage is only to be used for this purpose
24659      * @return Boolean
24660      */
24661     resetHasChanged : function()
24662     {
24663         this.items.each(function(f){
24664            f.resetHasChanged();
24665         });
24666         
24667     },
24668     
24669     
24670     /**
24671      * Performs a predefined action (submit or load) or custom actions you define on this form.
24672      * @param {String} actionName The name of the action type
24673      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24674      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24675      * accept other config options):
24676      * <pre>
24677 Property          Type             Description
24678 ----------------  ---------------  ----------------------------------------------------------------------------------
24679 url               String           The url for the action (defaults to the form's url)
24680 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24681 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24682 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24683                                    validate the form on the client (defaults to false)
24684      * </pre>
24685      * @return {BasicForm} this
24686      */
24687     doAction : function(action, options){
24688         if(typeof action == 'string'){
24689             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24690         }
24691         if(this.fireEvent('beforeaction', this, action) !== false){
24692             this.beforeAction(action);
24693             action.run.defer(100, action);
24694         }
24695         return this;
24696     },
24697
24698     /**
24699      * Shortcut to do a submit action.
24700      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24701      * @return {BasicForm} this
24702      */
24703     submit : function(options){
24704         this.doAction('submit', options);
24705         return this;
24706     },
24707
24708     /**
24709      * Shortcut to do a load action.
24710      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24711      * @return {BasicForm} this
24712      */
24713     load : function(options){
24714         this.doAction('load', options);
24715         return this;
24716     },
24717
24718     /**
24719      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24720      * @param {Record} record The record to edit
24721      * @return {BasicForm} this
24722      */
24723     updateRecord : function(record){
24724         record.beginEdit();
24725         var fs = record.fields;
24726         fs.each(function(f){
24727             var field = this.findField(f.name);
24728             if(field){
24729                 record.set(f.name, field.getValue());
24730             }
24731         }, this);
24732         record.endEdit();
24733         return this;
24734     },
24735
24736     /**
24737      * Loads an Roo.data.Record into this form.
24738      * @param {Record} record The record to load
24739      * @return {BasicForm} this
24740      */
24741     loadRecord : function(record){
24742         this.setValues(record.data);
24743         return this;
24744     },
24745
24746     // private
24747     beforeAction : function(action){
24748         var o = action.options;
24749         
24750         if(!this.disableMask) {
24751             if(this.waitMsgTarget === true){
24752                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24753             }else if(this.waitMsgTarget){
24754                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24755                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24756             }else {
24757                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24758             }
24759         }
24760         
24761          
24762     },
24763
24764     // private
24765     afterAction : function(action, success){
24766         this.activeAction = null;
24767         var o = action.options;
24768         
24769         if(!this.disableMask) {
24770             if(this.waitMsgTarget === true){
24771                 this.el.unmask();
24772             }else if(this.waitMsgTarget){
24773                 this.waitMsgTarget.unmask();
24774             }else{
24775                 Roo.MessageBox.updateProgress(1);
24776                 Roo.MessageBox.hide();
24777             }
24778         }
24779         
24780         if(success){
24781             if(o.reset){
24782                 this.reset();
24783             }
24784             Roo.callback(o.success, o.scope, [this, action]);
24785             this.fireEvent('actioncomplete', this, action);
24786             
24787         }else{
24788             
24789             // failure condition..
24790             // we have a scenario where updates need confirming.
24791             // eg. if a locking scenario exists..
24792             // we look for { errors : { needs_confirm : true }} in the response.
24793             if (
24794                 (typeof(action.result) != 'undefined')  &&
24795                 (typeof(action.result.errors) != 'undefined')  &&
24796                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24797            ){
24798                 var _t = this;
24799                 Roo.MessageBox.confirm(
24800                     "Change requires confirmation",
24801                     action.result.errorMsg,
24802                     function(r) {
24803                         if (r != 'yes') {
24804                             return;
24805                         }
24806                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24807                     }
24808                     
24809                 );
24810                 
24811                 
24812                 
24813                 return;
24814             }
24815             
24816             Roo.callback(o.failure, o.scope, [this, action]);
24817             // show an error message if no failed handler is set..
24818             if (!this.hasListener('actionfailed')) {
24819                 Roo.MessageBox.alert("Error",
24820                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24821                         action.result.errorMsg :
24822                         "Saving Failed, please check your entries or try again"
24823                 );
24824             }
24825             
24826             this.fireEvent('actionfailed', this, action);
24827         }
24828         
24829     },
24830
24831     /**
24832      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24833      * @param {String} id The value to search for
24834      * @return Field
24835      */
24836     findField : function(id){
24837         var field = this.items.get(id);
24838         if(!field){
24839             this.items.each(function(f){
24840                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24841                     field = f;
24842                     return false;
24843                 }
24844             });
24845         }
24846         return field || null;
24847     },
24848
24849     /**
24850      * Add a secondary form to this one, 
24851      * Used to provide tabbed forms. One form is primary, with hidden values 
24852      * which mirror the elements from the other forms.
24853      * 
24854      * @param {Roo.form.Form} form to add.
24855      * 
24856      */
24857     addForm : function(form)
24858     {
24859        
24860         if (this.childForms.indexOf(form) > -1) {
24861             // already added..
24862             return;
24863         }
24864         this.childForms.push(form);
24865         var n = '';
24866         Roo.each(form.allItems, function (fe) {
24867             
24868             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24869             if (this.findField(n)) { // already added..
24870                 return;
24871             }
24872             var add = new Roo.form.Hidden({
24873                 name : n
24874             });
24875             add.render(this.el);
24876             
24877             this.add( add );
24878         }, this);
24879         
24880     },
24881     /**
24882      * Mark fields in this form invalid in bulk.
24883      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24884      * @return {BasicForm} this
24885      */
24886     markInvalid : function(errors){
24887         if(errors instanceof Array){
24888             for(var i = 0, len = errors.length; i < len; i++){
24889                 var fieldError = errors[i];
24890                 var f = this.findField(fieldError.id);
24891                 if(f){
24892                     f.markInvalid(fieldError.msg);
24893                 }
24894             }
24895         }else{
24896             var field, id;
24897             for(id in errors){
24898                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24899                     field.markInvalid(errors[id]);
24900                 }
24901             }
24902         }
24903         Roo.each(this.childForms || [], function (f) {
24904             f.markInvalid(errors);
24905         });
24906         
24907         return this;
24908     },
24909
24910     /**
24911      * Set values for fields in this form in bulk.
24912      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24913      * @return {BasicForm} this
24914      */
24915     setValues : function(values){
24916         if(values instanceof Array){ // array of objects
24917             for(var i = 0, len = values.length; i < len; i++){
24918                 var v = values[i];
24919                 var f = this.findField(v.id);
24920                 if(f){
24921                     f.setValue(v.value);
24922                     if(this.trackResetOnLoad){
24923                         f.originalValue = f.getValue();
24924                     }
24925                 }
24926             }
24927         }else{ // object hash
24928             var field, id;
24929             for(id in values){
24930                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24931                     
24932                     if (field.setFromData && 
24933                         field.valueField && 
24934                         field.displayField &&
24935                         // combos' with local stores can 
24936                         // be queried via setValue()
24937                         // to set their value..
24938                         (field.store && !field.store.isLocal)
24939                         ) {
24940                         // it's a combo
24941                         var sd = { };
24942                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24943                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24944                         field.setFromData(sd);
24945                         
24946                     } else {
24947                         field.setValue(values[id]);
24948                     }
24949                     
24950                     
24951                     if(this.trackResetOnLoad){
24952                         field.originalValue = field.getValue();
24953                     }
24954                 }
24955             }
24956         }
24957         this.resetHasChanged();
24958         
24959         
24960         Roo.each(this.childForms || [], function (f) {
24961             f.setValues(values);
24962             f.resetHasChanged();
24963         });
24964                 
24965         return this;
24966     },
24967  
24968     /**
24969      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24970      * they are returned as an array.
24971      * @param {Boolean} asString
24972      * @return {Object}
24973      */
24974     getValues : function(asString){
24975         if (this.childForms) {
24976             // copy values from the child forms
24977             Roo.each(this.childForms, function (f) {
24978                 this.setValues(f.getValues());
24979             }, this);
24980         }
24981         
24982         // use formdata
24983         if (typeof(FormData) != 'undefined' && asString !== true) {
24984             // this relies on a 'recent' version of chrome apparently...
24985             try {
24986                 var fd = (new FormData(this.el.dom)).entries();
24987                 var ret = {};
24988                 var ent = fd.next();
24989                 while (!ent.done) {
24990                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
24991                     ent = fd.next();
24992                 };
24993                 return ret;
24994             } catch(e) {
24995                 
24996             }
24997             
24998         }
24999         
25000         
25001         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25002         if(asString === true){
25003             return fs;
25004         }
25005         return Roo.urlDecode(fs);
25006     },
25007     
25008     /**
25009      * Returns the fields in this form as an object with key/value pairs. 
25010      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25011      * @return {Object}
25012      */
25013     getFieldValues : function(with_hidden)
25014     {
25015         if (this.childForms) {
25016             // copy values from the child forms
25017             // should this call getFieldValues - probably not as we do not currently copy
25018             // hidden fields when we generate..
25019             Roo.each(this.childForms, function (f) {
25020                 this.setValues(f.getValues());
25021             }, this);
25022         }
25023         
25024         var ret = {};
25025         this.items.each(function(f){
25026             if (!f.getName()) {
25027                 return;
25028             }
25029             var v = f.getValue();
25030             if (f.inputType =='radio') {
25031                 if (typeof(ret[f.getName()]) == 'undefined') {
25032                     ret[f.getName()] = ''; // empty..
25033                 }
25034                 
25035                 if (!f.el.dom.checked) {
25036                     return;
25037                     
25038                 }
25039                 v = f.el.dom.value;
25040                 
25041             }
25042             
25043             // not sure if this supported any more..
25044             if ((typeof(v) == 'object') && f.getRawValue) {
25045                 v = f.getRawValue() ; // dates..
25046             }
25047             // combo boxes where name != hiddenName...
25048             if (f.name != f.getName()) {
25049                 ret[f.name] = f.getRawValue();
25050             }
25051             ret[f.getName()] = v;
25052         });
25053         
25054         return ret;
25055     },
25056
25057     /**
25058      * Clears all invalid messages in this form.
25059      * @return {BasicForm} this
25060      */
25061     clearInvalid : function(){
25062         this.items.each(function(f){
25063            f.clearInvalid();
25064         });
25065         
25066         Roo.each(this.childForms || [], function (f) {
25067             f.clearInvalid();
25068         });
25069         
25070         
25071         return this;
25072     },
25073
25074     /**
25075      * Resets this form.
25076      * @return {BasicForm} this
25077      */
25078     reset : function(){
25079         this.items.each(function(f){
25080             f.reset();
25081         });
25082         
25083         Roo.each(this.childForms || [], function (f) {
25084             f.reset();
25085         });
25086         this.resetHasChanged();
25087         
25088         return this;
25089     },
25090
25091     /**
25092      * Add Roo.form components to this form.
25093      * @param {Field} field1
25094      * @param {Field} field2 (optional)
25095      * @param {Field} etc (optional)
25096      * @return {BasicForm} this
25097      */
25098     add : function(){
25099         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25100         return this;
25101     },
25102
25103
25104     /**
25105      * Removes a field from the items collection (does NOT remove its markup).
25106      * @param {Field} field
25107      * @return {BasicForm} this
25108      */
25109     remove : function(field){
25110         this.items.remove(field);
25111         return this;
25112     },
25113
25114     /**
25115      * Looks at the fields in this form, checks them for an id attribute,
25116      * and calls applyTo on the existing dom element with that id.
25117      * @return {BasicForm} this
25118      */
25119     render : function(){
25120         this.items.each(function(f){
25121             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25122                 f.applyTo(f.id);
25123             }
25124         });
25125         return this;
25126     },
25127
25128     /**
25129      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25130      * @param {Object} values
25131      * @return {BasicForm} this
25132      */
25133     applyToFields : function(o){
25134         this.items.each(function(f){
25135            Roo.apply(f, o);
25136         });
25137         return this;
25138     },
25139
25140     /**
25141      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25142      * @param {Object} values
25143      * @return {BasicForm} this
25144      */
25145     applyIfToFields : function(o){
25146         this.items.each(function(f){
25147            Roo.applyIf(f, o);
25148         });
25149         return this;
25150     }
25151 });
25152
25153 // back compat
25154 Roo.BasicForm = Roo.form.BasicForm;
25155
25156 Roo.apply(Roo.form.BasicForm, {
25157     
25158     popover : {
25159         
25160         padding : 5,
25161         
25162         isApplied : false,
25163         
25164         isMasked : false,
25165         
25166         form : false,
25167         
25168         target : false,
25169         
25170         intervalID : false,
25171         
25172         maskEl : false,
25173         
25174         apply : function()
25175         {
25176             if(this.isApplied){
25177                 return;
25178             }
25179             
25180             this.maskEl = {
25181                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25182                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25183                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25184                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25185             };
25186             
25187             this.maskEl.top.enableDisplayMode("block");
25188             this.maskEl.left.enableDisplayMode("block");
25189             this.maskEl.bottom.enableDisplayMode("block");
25190             this.maskEl.right.enableDisplayMode("block");
25191             
25192             Roo.get(document.body).on('click', function(){
25193                 this.unmask();
25194             }, this);
25195             
25196             Roo.get(document.body).on('touchstart', function(){
25197                 this.unmask();
25198             }, this);
25199             
25200             this.isApplied = true
25201         },
25202         
25203         mask : function(form, target)
25204         {
25205             this.form = form;
25206             
25207             this.target = target;
25208             
25209             if(!this.form.errorMask || !target.el){
25210                 return;
25211             }
25212             
25213             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25214             
25215             var ot = this.target.el.calcOffsetsTo(scrollable);
25216             
25217             var scrollTo = ot[1] - this.form.maskOffset;
25218             
25219             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25220             
25221             scrollable.scrollTo('top', scrollTo);
25222             
25223             var el = this.target.wrap || this.target.el;
25224             
25225             var box = el.getBox();
25226             
25227             this.maskEl.top.setStyle('position', 'absolute');
25228             this.maskEl.top.setStyle('z-index', 10000);
25229             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25230             this.maskEl.top.setLeft(0);
25231             this.maskEl.top.setTop(0);
25232             this.maskEl.top.show();
25233             
25234             this.maskEl.left.setStyle('position', 'absolute');
25235             this.maskEl.left.setStyle('z-index', 10000);
25236             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25237             this.maskEl.left.setLeft(0);
25238             this.maskEl.left.setTop(box.y - this.padding);
25239             this.maskEl.left.show();
25240
25241             this.maskEl.bottom.setStyle('position', 'absolute');
25242             this.maskEl.bottom.setStyle('z-index', 10000);
25243             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25244             this.maskEl.bottom.setLeft(0);
25245             this.maskEl.bottom.setTop(box.bottom + this.padding);
25246             this.maskEl.bottom.show();
25247
25248             this.maskEl.right.setStyle('position', 'absolute');
25249             this.maskEl.right.setStyle('z-index', 10000);
25250             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25251             this.maskEl.right.setLeft(box.right + this.padding);
25252             this.maskEl.right.setTop(box.y - this.padding);
25253             this.maskEl.right.show();
25254
25255             this.intervalID = window.setInterval(function() {
25256                 Roo.form.BasicForm.popover.unmask();
25257             }, 10000);
25258
25259             window.onwheel = function(){ return false;};
25260             
25261             (function(){ this.isMasked = true; }).defer(500, this);
25262             
25263         },
25264         
25265         unmask : function()
25266         {
25267             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25268                 return;
25269             }
25270             
25271             this.maskEl.top.setStyle('position', 'absolute');
25272             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25273             this.maskEl.top.hide();
25274
25275             this.maskEl.left.setStyle('position', 'absolute');
25276             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25277             this.maskEl.left.hide();
25278
25279             this.maskEl.bottom.setStyle('position', 'absolute');
25280             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25281             this.maskEl.bottom.hide();
25282
25283             this.maskEl.right.setStyle('position', 'absolute');
25284             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25285             this.maskEl.right.hide();
25286             
25287             window.onwheel = function(){ return true;};
25288             
25289             if(this.intervalID){
25290                 window.clearInterval(this.intervalID);
25291                 this.intervalID = false;
25292             }
25293             
25294             this.isMasked = false;
25295             
25296         }
25297         
25298     }
25299     
25300 });/*
25301  * Based on:
25302  * Ext JS Library 1.1.1
25303  * Copyright(c) 2006-2007, Ext JS, LLC.
25304  *
25305  * Originally Released Under LGPL - original licence link has changed is not relivant.
25306  *
25307  * Fork - LGPL
25308  * <script type="text/javascript">
25309  */
25310
25311 /**
25312  * @class Roo.form.Form
25313  * @extends Roo.form.BasicForm
25314  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25315  * @constructor
25316  * @param {Object} config Configuration options
25317  */
25318 Roo.form.Form = function(config){
25319     var xitems =  [];
25320     if (config.items) {
25321         xitems = config.items;
25322         delete config.items;
25323     }
25324    
25325     
25326     Roo.form.Form.superclass.constructor.call(this, null, config);
25327     this.url = this.url || this.action;
25328     if(!this.root){
25329         this.root = new Roo.form.Layout(Roo.applyIf({
25330             id: Roo.id()
25331         }, config));
25332     }
25333     this.active = this.root;
25334     /**
25335      * Array of all the buttons that have been added to this form via {@link addButton}
25336      * @type Array
25337      */
25338     this.buttons = [];
25339     this.allItems = [];
25340     this.addEvents({
25341         /**
25342          * @event clientvalidation
25343          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25344          * @param {Form} this
25345          * @param {Boolean} valid true if the form has passed client-side validation
25346          */
25347         clientvalidation: true,
25348         /**
25349          * @event rendered
25350          * Fires when the form is rendered
25351          * @param {Roo.form.Form} form
25352          */
25353         rendered : true
25354     });
25355     
25356     if (this.progressUrl) {
25357             // push a hidden field onto the list of fields..
25358             this.addxtype( {
25359                     xns: Roo.form, 
25360                     xtype : 'Hidden', 
25361                     name : 'UPLOAD_IDENTIFIER' 
25362             });
25363         }
25364         
25365     
25366     Roo.each(xitems, this.addxtype, this);
25367     
25368 };
25369
25370 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25371     /**
25372      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25373      */
25374     /**
25375      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25376      */
25377     /**
25378      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25379      */
25380     buttonAlign:'center',
25381
25382     /**
25383      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25384      */
25385     minButtonWidth:75,
25386
25387     /**
25388      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25389      * This property cascades to child containers if not set.
25390      */
25391     labelAlign:'left',
25392
25393     /**
25394      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25395      * fires a looping event with that state. This is required to bind buttons to the valid
25396      * state using the config value formBind:true on the button.
25397      */
25398     monitorValid : false,
25399
25400     /**
25401      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25402      */
25403     monitorPoll : 200,
25404     
25405     /**
25406      * @cfg {String} progressUrl - Url to return progress data 
25407      */
25408     
25409     progressUrl : false,
25410     /**
25411      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25412      * sending a formdata with extra parameters - eg uploaded elements.
25413      */
25414     
25415     formData : false,
25416     
25417     /**
25418      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25419      * fields are added and the column is closed. If no fields are passed the column remains open
25420      * until end() is called.
25421      * @param {Object} config The config to pass to the column
25422      * @param {Field} field1 (optional)
25423      * @param {Field} field2 (optional)
25424      * @param {Field} etc (optional)
25425      * @return Column The column container object
25426      */
25427     column : function(c){
25428         var col = new Roo.form.Column(c);
25429         this.start(col);
25430         if(arguments.length > 1){ // duplicate code required because of Opera
25431             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25432             this.end();
25433         }
25434         return col;
25435     },
25436
25437     /**
25438      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25439      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25440      * until end() is called.
25441      * @param {Object} config The config to pass to the fieldset
25442      * @param {Field} field1 (optional)
25443      * @param {Field} field2 (optional)
25444      * @param {Field} etc (optional)
25445      * @return FieldSet The fieldset container object
25446      */
25447     fieldset : function(c){
25448         var fs = new Roo.form.FieldSet(c);
25449         this.start(fs);
25450         if(arguments.length > 1){ // duplicate code required because of Opera
25451             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25452             this.end();
25453         }
25454         return fs;
25455     },
25456
25457     /**
25458      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25459      * fields are added and the container is closed. If no fields are passed the container remains open
25460      * until end() is called.
25461      * @param {Object} config The config to pass to the Layout
25462      * @param {Field} field1 (optional)
25463      * @param {Field} field2 (optional)
25464      * @param {Field} etc (optional)
25465      * @return Layout The container object
25466      */
25467     container : function(c){
25468         var l = new Roo.form.Layout(c);
25469         this.start(l);
25470         if(arguments.length > 1){ // duplicate code required because of Opera
25471             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25472             this.end();
25473         }
25474         return l;
25475     },
25476
25477     /**
25478      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25479      * @param {Object} container A Roo.form.Layout or subclass of Layout
25480      * @return {Form} this
25481      */
25482     start : function(c){
25483         // cascade label info
25484         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25485         this.active.stack.push(c);
25486         c.ownerCt = this.active;
25487         this.active = c;
25488         return this;
25489     },
25490
25491     /**
25492      * Closes the current open container
25493      * @return {Form} this
25494      */
25495     end : function(){
25496         if(this.active == this.root){
25497             return this;
25498         }
25499         this.active = this.active.ownerCt;
25500         return this;
25501     },
25502
25503     /**
25504      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25505      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25506      * as the label of the field.
25507      * @param {Field} field1
25508      * @param {Field} field2 (optional)
25509      * @param {Field} etc. (optional)
25510      * @return {Form} this
25511      */
25512     add : function(){
25513         this.active.stack.push.apply(this.active.stack, arguments);
25514         this.allItems.push.apply(this.allItems,arguments);
25515         var r = [];
25516         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25517             if(a[i].isFormField){
25518                 r.push(a[i]);
25519             }
25520         }
25521         if(r.length > 0){
25522             Roo.form.Form.superclass.add.apply(this, r);
25523         }
25524         return this;
25525     },
25526     
25527
25528     
25529     
25530     
25531      /**
25532      * Find any element that has been added to a form, using it's ID or name
25533      * This can include framesets, columns etc. along with regular fields..
25534      * @param {String} id - id or name to find.
25535      
25536      * @return {Element} e - or false if nothing found.
25537      */
25538     findbyId : function(id)
25539     {
25540         var ret = false;
25541         if (!id) {
25542             return ret;
25543         }
25544         Roo.each(this.allItems, function(f){
25545             if (f.id == id || f.name == id ){
25546                 ret = f;
25547                 return false;
25548             }
25549         });
25550         return ret;
25551     },
25552
25553     
25554     
25555     /**
25556      * Render this form into the passed container. This should only be called once!
25557      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25558      * @return {Form} this
25559      */
25560     render : function(ct)
25561     {
25562         
25563         
25564         
25565         ct = Roo.get(ct);
25566         var o = this.autoCreate || {
25567             tag: 'form',
25568             method : this.method || 'POST',
25569             id : this.id || Roo.id()
25570         };
25571         this.initEl(ct.createChild(o));
25572
25573         this.root.render(this.el);
25574         
25575        
25576              
25577         this.items.each(function(f){
25578             f.render('x-form-el-'+f.id);
25579         });
25580
25581         if(this.buttons.length > 0){
25582             // tables are required to maintain order and for correct IE layout
25583             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25584                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25585                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25586             }}, null, true);
25587             var tr = tb.getElementsByTagName('tr')[0];
25588             for(var i = 0, len = this.buttons.length; i < len; i++) {
25589                 var b = this.buttons[i];
25590                 var td = document.createElement('td');
25591                 td.className = 'x-form-btn-td';
25592                 b.render(tr.appendChild(td));
25593             }
25594         }
25595         if(this.monitorValid){ // initialize after render
25596             this.startMonitoring();
25597         }
25598         this.fireEvent('rendered', this);
25599         return this;
25600     },
25601
25602     /**
25603      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25604      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25605      * object or a valid Roo.DomHelper element config
25606      * @param {Function} handler The function called when the button is clicked
25607      * @param {Object} scope (optional) The scope of the handler function
25608      * @return {Roo.Button}
25609      */
25610     addButton : function(config, handler, scope){
25611         var bc = {
25612             handler: handler,
25613             scope: scope,
25614             minWidth: this.minButtonWidth,
25615             hideParent:true
25616         };
25617         if(typeof config == "string"){
25618             bc.text = config;
25619         }else{
25620             Roo.apply(bc, config);
25621         }
25622         var btn = new Roo.Button(null, bc);
25623         this.buttons.push(btn);
25624         return btn;
25625     },
25626
25627      /**
25628      * Adds a series of form elements (using the xtype property as the factory method.
25629      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25630      * @param {Object} config 
25631      */
25632     
25633     addxtype : function()
25634     {
25635         var ar = Array.prototype.slice.call(arguments, 0);
25636         var ret = false;
25637         for(var i = 0; i < ar.length; i++) {
25638             if (!ar[i]) {
25639                 continue; // skip -- if this happends something invalid got sent, we 
25640                 // should ignore it, as basically that interface element will not show up
25641                 // and that should be pretty obvious!!
25642             }
25643             
25644             if (Roo.form[ar[i].xtype]) {
25645                 ar[i].form = this;
25646                 var fe = Roo.factory(ar[i], Roo.form);
25647                 if (!ret) {
25648                     ret = fe;
25649                 }
25650                 fe.form = this;
25651                 if (fe.store) {
25652                     fe.store.form = this;
25653                 }
25654                 if (fe.isLayout) {  
25655                          
25656                     this.start(fe);
25657                     this.allItems.push(fe);
25658                     if (fe.items && fe.addxtype) {
25659                         fe.addxtype.apply(fe, fe.items);
25660                         delete fe.items;
25661                     }
25662                      this.end();
25663                     continue;
25664                 }
25665                 
25666                 
25667                  
25668                 this.add(fe);
25669               //  console.log('adding ' + ar[i].xtype);
25670             }
25671             if (ar[i].xtype == 'Button') {  
25672                 //console.log('adding button');
25673                 //console.log(ar[i]);
25674                 this.addButton(ar[i]);
25675                 this.allItems.push(fe);
25676                 continue;
25677             }
25678             
25679             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25680                 alert('end is not supported on xtype any more, use items');
25681             //    this.end();
25682             //    //console.log('adding end');
25683             }
25684             
25685         }
25686         return ret;
25687     },
25688     
25689     /**
25690      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25691      * option "monitorValid"
25692      */
25693     startMonitoring : function(){
25694         if(!this.bound){
25695             this.bound = true;
25696             Roo.TaskMgr.start({
25697                 run : this.bindHandler,
25698                 interval : this.monitorPoll || 200,
25699                 scope: this
25700             });
25701         }
25702     },
25703
25704     /**
25705      * Stops monitoring of the valid state of this form
25706      */
25707     stopMonitoring : function(){
25708         this.bound = false;
25709     },
25710
25711     // private
25712     bindHandler : function(){
25713         if(!this.bound){
25714             return false; // stops binding
25715         }
25716         var valid = true;
25717         this.items.each(function(f){
25718             if(!f.isValid(true)){
25719                 valid = false;
25720                 return false;
25721             }
25722         });
25723         for(var i = 0, len = this.buttons.length; i < len; i++){
25724             var btn = this.buttons[i];
25725             if(btn.formBind === true && btn.disabled === valid){
25726                 btn.setDisabled(!valid);
25727             }
25728         }
25729         this.fireEvent('clientvalidation', this, valid);
25730     }
25731     
25732     
25733     
25734     
25735     
25736     
25737     
25738     
25739 });
25740
25741
25742 // back compat
25743 Roo.Form = Roo.form.Form;
25744 /*
25745  * Based on:
25746  * Ext JS Library 1.1.1
25747  * Copyright(c) 2006-2007, Ext JS, LLC.
25748  *
25749  * Originally Released Under LGPL - original licence link has changed is not relivant.
25750  *
25751  * Fork - LGPL
25752  * <script type="text/javascript">
25753  */
25754
25755 // as we use this in bootstrap.
25756 Roo.namespace('Roo.form');
25757  /**
25758  * @class Roo.form.Action
25759  * Internal Class used to handle form actions
25760  * @constructor
25761  * @param {Roo.form.BasicForm} el The form element or its id
25762  * @param {Object} config Configuration options
25763  */
25764
25765  
25766  
25767 // define the action interface
25768 Roo.form.Action = function(form, options){
25769     this.form = form;
25770     this.options = options || {};
25771 };
25772 /**
25773  * Client Validation Failed
25774  * @const 
25775  */
25776 Roo.form.Action.CLIENT_INVALID = 'client';
25777 /**
25778  * Server Validation Failed
25779  * @const 
25780  */
25781 Roo.form.Action.SERVER_INVALID = 'server';
25782  /**
25783  * Connect to Server Failed
25784  * @const 
25785  */
25786 Roo.form.Action.CONNECT_FAILURE = 'connect';
25787 /**
25788  * Reading Data from Server Failed
25789  * @const 
25790  */
25791 Roo.form.Action.LOAD_FAILURE = 'load';
25792
25793 Roo.form.Action.prototype = {
25794     type : 'default',
25795     failureType : undefined,
25796     response : undefined,
25797     result : undefined,
25798
25799     // interface method
25800     run : function(options){
25801
25802     },
25803
25804     // interface method
25805     success : function(response){
25806
25807     },
25808
25809     // interface method
25810     handleResponse : function(response){
25811
25812     },
25813
25814     // default connection failure
25815     failure : function(response){
25816         
25817         this.response = response;
25818         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25819         this.form.afterAction(this, false);
25820     },
25821
25822     processResponse : function(response){
25823         this.response = response;
25824         if(!response.responseText){
25825             return true;
25826         }
25827         this.result = this.handleResponse(response);
25828         return this.result;
25829     },
25830
25831     // utility functions used internally
25832     getUrl : function(appendParams){
25833         var url = this.options.url || this.form.url || this.form.el.dom.action;
25834         if(appendParams){
25835             var p = this.getParams();
25836             if(p){
25837                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25838             }
25839         }
25840         return url;
25841     },
25842
25843     getMethod : function(){
25844         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25845     },
25846
25847     getParams : function(){
25848         var bp = this.form.baseParams;
25849         var p = this.options.params;
25850         if(p){
25851             if(typeof p == "object"){
25852                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25853             }else if(typeof p == 'string' && bp){
25854                 p += '&' + Roo.urlEncode(bp);
25855             }
25856         }else if(bp){
25857             p = Roo.urlEncode(bp);
25858         }
25859         return p;
25860     },
25861
25862     createCallback : function(){
25863         return {
25864             success: this.success,
25865             failure: this.failure,
25866             scope: this,
25867             timeout: (this.form.timeout*1000),
25868             upload: this.form.fileUpload ? this.success : undefined
25869         };
25870     }
25871 };
25872
25873 Roo.form.Action.Submit = function(form, options){
25874     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25875 };
25876
25877 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25878     type : 'submit',
25879
25880     haveProgress : false,
25881     uploadComplete : false,
25882     
25883     // uploadProgress indicator.
25884     uploadProgress : function()
25885     {
25886         if (!this.form.progressUrl) {
25887             return;
25888         }
25889         
25890         if (!this.haveProgress) {
25891             Roo.MessageBox.progress("Uploading", "Uploading");
25892         }
25893         if (this.uploadComplete) {
25894            Roo.MessageBox.hide();
25895            return;
25896         }
25897         
25898         this.haveProgress = true;
25899    
25900         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25901         
25902         var c = new Roo.data.Connection();
25903         c.request({
25904             url : this.form.progressUrl,
25905             params: {
25906                 id : uid
25907             },
25908             method: 'GET',
25909             success : function(req){
25910                //console.log(data);
25911                 var rdata = false;
25912                 var edata;
25913                 try  {
25914                    rdata = Roo.decode(req.responseText)
25915                 } catch (e) {
25916                     Roo.log("Invalid data from server..");
25917                     Roo.log(edata);
25918                     return;
25919                 }
25920                 if (!rdata || !rdata.success) {
25921                     Roo.log(rdata);
25922                     Roo.MessageBox.alert(Roo.encode(rdata));
25923                     return;
25924                 }
25925                 var data = rdata.data;
25926                 
25927                 if (this.uploadComplete) {
25928                    Roo.MessageBox.hide();
25929                    return;
25930                 }
25931                    
25932                 if (data){
25933                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25934                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25935                     );
25936                 }
25937                 this.uploadProgress.defer(2000,this);
25938             },
25939        
25940             failure: function(data) {
25941                 Roo.log('progress url failed ');
25942                 Roo.log(data);
25943             },
25944             scope : this
25945         });
25946            
25947     },
25948     
25949     
25950     run : function()
25951     {
25952         // run get Values on the form, so it syncs any secondary forms.
25953         this.form.getValues();
25954         
25955         var o = this.options;
25956         var method = this.getMethod();
25957         var isPost = method == 'POST';
25958         if(o.clientValidation === false || this.form.isValid()){
25959             
25960             if (this.form.progressUrl) {
25961                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25962                     (new Date() * 1) + '' + Math.random());
25963                     
25964             } 
25965             
25966             
25967             Roo.Ajax.request(Roo.apply(this.createCallback(), {
25968                 form:this.form.el.dom,
25969                 url:this.getUrl(!isPost),
25970                 method: method,
25971                 params:isPost ? this.getParams() : null,
25972                 isUpload: this.form.fileUpload,
25973                 formData : this.form.formData
25974             }));
25975             
25976             this.uploadProgress();
25977
25978         }else if (o.clientValidation !== false){ // client validation failed
25979             this.failureType = Roo.form.Action.CLIENT_INVALID;
25980             this.form.afterAction(this, false);
25981         }
25982     },
25983
25984     success : function(response)
25985     {
25986         this.uploadComplete= true;
25987         if (this.haveProgress) {
25988             Roo.MessageBox.hide();
25989         }
25990         
25991         
25992         var result = this.processResponse(response);
25993         if(result === true || result.success){
25994             this.form.afterAction(this, true);
25995             return;
25996         }
25997         if(result.errors){
25998             this.form.markInvalid(result.errors);
25999             this.failureType = Roo.form.Action.SERVER_INVALID;
26000         }
26001         this.form.afterAction(this, false);
26002     },
26003     failure : function(response)
26004     {
26005         this.uploadComplete= true;
26006         if (this.haveProgress) {
26007             Roo.MessageBox.hide();
26008         }
26009         
26010         this.response = response;
26011         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26012         this.form.afterAction(this, false);
26013     },
26014     
26015     handleResponse : function(response){
26016         if(this.form.errorReader){
26017             var rs = this.form.errorReader.read(response);
26018             var errors = [];
26019             if(rs.records){
26020                 for(var i = 0, len = rs.records.length; i < len; i++) {
26021                     var r = rs.records[i];
26022                     errors[i] = r.data;
26023                 }
26024             }
26025             if(errors.length < 1){
26026                 errors = null;
26027             }
26028             return {
26029                 success : rs.success,
26030                 errors : errors
26031             };
26032         }
26033         var ret = false;
26034         try {
26035             ret = Roo.decode(response.responseText);
26036         } catch (e) {
26037             ret = {
26038                 success: false,
26039                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26040                 errors : []
26041             };
26042         }
26043         return ret;
26044         
26045     }
26046 });
26047
26048
26049 Roo.form.Action.Load = function(form, options){
26050     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26051     this.reader = this.form.reader;
26052 };
26053
26054 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26055     type : 'load',
26056
26057     run : function(){
26058         
26059         Roo.Ajax.request(Roo.apply(
26060                 this.createCallback(), {
26061                     method:this.getMethod(),
26062                     url:this.getUrl(false),
26063                     params:this.getParams()
26064         }));
26065     },
26066
26067     success : function(response){
26068         
26069         var result = this.processResponse(response);
26070         if(result === true || !result.success || !result.data){
26071             this.failureType = Roo.form.Action.LOAD_FAILURE;
26072             this.form.afterAction(this, false);
26073             return;
26074         }
26075         this.form.clearInvalid();
26076         this.form.setValues(result.data);
26077         this.form.afterAction(this, true);
26078     },
26079
26080     handleResponse : function(response){
26081         if(this.form.reader){
26082             var rs = this.form.reader.read(response);
26083             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26084             return {
26085                 success : rs.success,
26086                 data : data
26087             };
26088         }
26089         return Roo.decode(response.responseText);
26090     }
26091 });
26092
26093 Roo.form.Action.ACTION_TYPES = {
26094     'load' : Roo.form.Action.Load,
26095     'submit' : Roo.form.Action.Submit
26096 };/*
26097  * Based on:
26098  * Ext JS Library 1.1.1
26099  * Copyright(c) 2006-2007, Ext JS, LLC.
26100  *
26101  * Originally Released Under LGPL - original licence link has changed is not relivant.
26102  *
26103  * Fork - LGPL
26104  * <script type="text/javascript">
26105  */
26106  
26107 /**
26108  * @class Roo.form.Layout
26109  * @extends Roo.Component
26110  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26111  * @constructor
26112  * @param {Object} config Configuration options
26113  */
26114 Roo.form.Layout = function(config){
26115     var xitems = [];
26116     if (config.items) {
26117         xitems = config.items;
26118         delete config.items;
26119     }
26120     Roo.form.Layout.superclass.constructor.call(this, config);
26121     this.stack = [];
26122     Roo.each(xitems, this.addxtype, this);
26123      
26124 };
26125
26126 Roo.extend(Roo.form.Layout, Roo.Component, {
26127     /**
26128      * @cfg {String/Object} autoCreate
26129      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26130      */
26131     /**
26132      * @cfg {String/Object/Function} style
26133      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26134      * a function which returns such a specification.
26135      */
26136     /**
26137      * @cfg {String} labelAlign
26138      * Valid values are "left," "top" and "right" (defaults to "left")
26139      */
26140     /**
26141      * @cfg {Number} labelWidth
26142      * Fixed width in pixels of all field labels (defaults to undefined)
26143      */
26144     /**
26145      * @cfg {Boolean} clear
26146      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26147      */
26148     clear : true,
26149     /**
26150      * @cfg {String} labelSeparator
26151      * The separator to use after field labels (defaults to ':')
26152      */
26153     labelSeparator : ':',
26154     /**
26155      * @cfg {Boolean} hideLabels
26156      * True to suppress the display of field labels in this layout (defaults to false)
26157      */
26158     hideLabels : false,
26159
26160     // private
26161     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26162     
26163     isLayout : true,
26164     
26165     // private
26166     onRender : function(ct, position){
26167         if(this.el){ // from markup
26168             this.el = Roo.get(this.el);
26169         }else {  // generate
26170             var cfg = this.getAutoCreate();
26171             this.el = ct.createChild(cfg, position);
26172         }
26173         if(this.style){
26174             this.el.applyStyles(this.style);
26175         }
26176         if(this.labelAlign){
26177             this.el.addClass('x-form-label-'+this.labelAlign);
26178         }
26179         if(this.hideLabels){
26180             this.labelStyle = "display:none";
26181             this.elementStyle = "padding-left:0;";
26182         }else{
26183             if(typeof this.labelWidth == 'number'){
26184                 this.labelStyle = "width:"+this.labelWidth+"px;";
26185                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26186             }
26187             if(this.labelAlign == 'top'){
26188                 this.labelStyle = "width:auto;";
26189                 this.elementStyle = "padding-left:0;";
26190             }
26191         }
26192         var stack = this.stack;
26193         var slen = stack.length;
26194         if(slen > 0){
26195             if(!this.fieldTpl){
26196                 var t = new Roo.Template(
26197                     '<div class="x-form-item {5}">',
26198                         '<label for="{0}" style="{2}">{1}{4}</label>',
26199                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26200                         '</div>',
26201                     '</div><div class="x-form-clear-left"></div>'
26202                 );
26203                 t.disableFormats = true;
26204                 t.compile();
26205                 Roo.form.Layout.prototype.fieldTpl = t;
26206             }
26207             for(var i = 0; i < slen; i++) {
26208                 if(stack[i].isFormField){
26209                     this.renderField(stack[i]);
26210                 }else{
26211                     this.renderComponent(stack[i]);
26212                 }
26213             }
26214         }
26215         if(this.clear){
26216             this.el.createChild({cls:'x-form-clear'});
26217         }
26218     },
26219
26220     // private
26221     renderField : function(f){
26222         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26223                f.id, //0
26224                f.fieldLabel, //1
26225                f.labelStyle||this.labelStyle||'', //2
26226                this.elementStyle||'', //3
26227                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26228                f.itemCls||this.itemCls||''  //5
26229        ], true).getPrevSibling());
26230     },
26231
26232     // private
26233     renderComponent : function(c){
26234         c.render(c.isLayout ? this.el : this.el.createChild());    
26235     },
26236     /**
26237      * Adds a object form elements (using the xtype property as the factory method.)
26238      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26239      * @param {Object} config 
26240      */
26241     addxtype : function(o)
26242     {
26243         // create the lement.
26244         o.form = this.form;
26245         var fe = Roo.factory(o, Roo.form);
26246         this.form.allItems.push(fe);
26247         this.stack.push(fe);
26248         
26249         if (fe.isFormField) {
26250             this.form.items.add(fe);
26251         }
26252          
26253         return fe;
26254     }
26255 });
26256
26257 /**
26258  * @class Roo.form.Column
26259  * @extends Roo.form.Layout
26260  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26261  * @constructor
26262  * @param {Object} config Configuration options
26263  */
26264 Roo.form.Column = function(config){
26265     Roo.form.Column.superclass.constructor.call(this, config);
26266 };
26267
26268 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26269     /**
26270      * @cfg {Number/String} width
26271      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26272      */
26273     /**
26274      * @cfg {String/Object} autoCreate
26275      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26276      */
26277
26278     // private
26279     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26280
26281     // private
26282     onRender : function(ct, position){
26283         Roo.form.Column.superclass.onRender.call(this, ct, position);
26284         if(this.width){
26285             this.el.setWidth(this.width);
26286         }
26287     }
26288 });
26289
26290
26291 /**
26292  * @class Roo.form.Row
26293  * @extends Roo.form.Layout
26294  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26295  * @constructor
26296  * @param {Object} config Configuration options
26297  */
26298
26299  
26300 Roo.form.Row = function(config){
26301     Roo.form.Row.superclass.constructor.call(this, config);
26302 };
26303  
26304 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26305       /**
26306      * @cfg {Number/String} width
26307      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26308      */
26309     /**
26310      * @cfg {Number/String} height
26311      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26312      */
26313     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26314     
26315     padWidth : 20,
26316     // private
26317     onRender : function(ct, position){
26318         //console.log('row render');
26319         if(!this.rowTpl){
26320             var t = new Roo.Template(
26321                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26322                     '<label for="{0}" style="{2}">{1}{4}</label>',
26323                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26324                     '</div>',
26325                 '</div>'
26326             );
26327             t.disableFormats = true;
26328             t.compile();
26329             Roo.form.Layout.prototype.rowTpl = t;
26330         }
26331         this.fieldTpl = this.rowTpl;
26332         
26333         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26334         var labelWidth = 100;
26335         
26336         if ((this.labelAlign != 'top')) {
26337             if (typeof this.labelWidth == 'number') {
26338                 labelWidth = this.labelWidth
26339             }
26340             this.padWidth =  20 + labelWidth;
26341             
26342         }
26343         
26344         Roo.form.Column.superclass.onRender.call(this, ct, position);
26345         if(this.width){
26346             this.el.setWidth(this.width);
26347         }
26348         if(this.height){
26349             this.el.setHeight(this.height);
26350         }
26351     },
26352     
26353     // private
26354     renderField : function(f){
26355         f.fieldEl = this.fieldTpl.append(this.el, [
26356                f.id, f.fieldLabel,
26357                f.labelStyle||this.labelStyle||'',
26358                this.elementStyle||'',
26359                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26360                f.itemCls||this.itemCls||'',
26361                f.width ? f.width + this.padWidth : 160 + this.padWidth
26362        ],true);
26363     }
26364 });
26365  
26366
26367 /**
26368  * @class Roo.form.FieldSet
26369  * @extends Roo.form.Layout
26370  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26371  * @constructor
26372  * @param {Object} config Configuration options
26373  */
26374 Roo.form.FieldSet = function(config){
26375     Roo.form.FieldSet.superclass.constructor.call(this, config);
26376 };
26377
26378 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26379     /**
26380      * @cfg {String} legend
26381      * The text to display as the legend for the FieldSet (defaults to '')
26382      */
26383     /**
26384      * @cfg {String/Object} autoCreate
26385      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26386      */
26387
26388     // private
26389     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26390
26391     // private
26392     onRender : function(ct, position){
26393         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26394         if(this.legend){
26395             this.setLegend(this.legend);
26396         }
26397     },
26398
26399     // private
26400     setLegend : function(text){
26401         if(this.rendered){
26402             this.el.child('legend').update(text);
26403         }
26404     }
26405 });/*
26406  * Based on:
26407  * Ext JS Library 1.1.1
26408  * Copyright(c) 2006-2007, Ext JS, LLC.
26409  *
26410  * Originally Released Under LGPL - original licence link has changed is not relivant.
26411  *
26412  * Fork - LGPL
26413  * <script type="text/javascript">
26414  */
26415 /**
26416  * @class Roo.form.VTypes
26417  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26418  * @singleton
26419  */
26420 Roo.form.VTypes = function(){
26421     // closure these in so they are only created once.
26422     var alpha = /^[a-zA-Z_]+$/;
26423     var alphanum = /^[a-zA-Z0-9_]+$/;
26424     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26425     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26426
26427     // All these messages and functions are configurable
26428     return {
26429         /**
26430          * The function used to validate email addresses
26431          * @param {String} value The email address
26432          */
26433         'email' : function(v){
26434             return email.test(v);
26435         },
26436         /**
26437          * The error text to display when the email validation function returns false
26438          * @type String
26439          */
26440         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26441         /**
26442          * The keystroke filter mask to be applied on email input
26443          * @type RegExp
26444          */
26445         'emailMask' : /[a-z0-9_\.\-@]/i,
26446
26447         /**
26448          * The function used to validate URLs
26449          * @param {String} value The URL
26450          */
26451         'url' : function(v){
26452             return url.test(v);
26453         },
26454         /**
26455          * The error text to display when the url validation function returns false
26456          * @type String
26457          */
26458         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26459         
26460         /**
26461          * The function used to validate alpha values
26462          * @param {String} value The value
26463          */
26464         'alpha' : function(v){
26465             return alpha.test(v);
26466         },
26467         /**
26468          * The error text to display when the alpha validation function returns false
26469          * @type String
26470          */
26471         'alphaText' : 'This field should only contain letters and _',
26472         /**
26473          * The keystroke filter mask to be applied on alpha input
26474          * @type RegExp
26475          */
26476         'alphaMask' : /[a-z_]/i,
26477
26478         /**
26479          * The function used to validate alphanumeric values
26480          * @param {String} value The value
26481          */
26482         'alphanum' : function(v){
26483             return alphanum.test(v);
26484         },
26485         /**
26486          * The error text to display when the alphanumeric validation function returns false
26487          * @type String
26488          */
26489         'alphanumText' : 'This field should only contain letters, numbers and _',
26490         /**
26491          * The keystroke filter mask to be applied on alphanumeric input
26492          * @type RegExp
26493          */
26494         'alphanumMask' : /[a-z0-9_]/i
26495     };
26496 }();//<script type="text/javascript">
26497
26498 /**
26499  * @class Roo.form.FCKeditor
26500  * @extends Roo.form.TextArea
26501  * Wrapper around the FCKEditor http://www.fckeditor.net
26502  * @constructor
26503  * Creates a new FCKeditor
26504  * @param {Object} config Configuration options
26505  */
26506 Roo.form.FCKeditor = function(config){
26507     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26508     this.addEvents({
26509          /**
26510          * @event editorinit
26511          * Fired when the editor is initialized - you can add extra handlers here..
26512          * @param {FCKeditor} this
26513          * @param {Object} the FCK object.
26514          */
26515         editorinit : true
26516     });
26517     
26518     
26519 };
26520 Roo.form.FCKeditor.editors = { };
26521 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26522 {
26523     //defaultAutoCreate : {
26524     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26525     //},
26526     // private
26527     /**
26528      * @cfg {Object} fck options - see fck manual for details.
26529      */
26530     fckconfig : false,
26531     
26532     /**
26533      * @cfg {Object} fck toolbar set (Basic or Default)
26534      */
26535     toolbarSet : 'Basic',
26536     /**
26537      * @cfg {Object} fck BasePath
26538      */ 
26539     basePath : '/fckeditor/',
26540     
26541     
26542     frame : false,
26543     
26544     value : '',
26545     
26546    
26547     onRender : function(ct, position)
26548     {
26549         if(!this.el){
26550             this.defaultAutoCreate = {
26551                 tag: "textarea",
26552                 style:"width:300px;height:60px;",
26553                 autocomplete: "new-password"
26554             };
26555         }
26556         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26557         /*
26558         if(this.grow){
26559             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26560             if(this.preventScrollbars){
26561                 this.el.setStyle("overflow", "hidden");
26562             }
26563             this.el.setHeight(this.growMin);
26564         }
26565         */
26566         //console.log('onrender' + this.getId() );
26567         Roo.form.FCKeditor.editors[this.getId()] = this;
26568          
26569
26570         this.replaceTextarea() ;
26571         
26572     },
26573     
26574     getEditor : function() {
26575         return this.fckEditor;
26576     },
26577     /**
26578      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26579      * @param {Mixed} value The value to set
26580      */
26581     
26582     
26583     setValue : function(value)
26584     {
26585         //console.log('setValue: ' + value);
26586         
26587         if(typeof(value) == 'undefined') { // not sure why this is happending...
26588             return;
26589         }
26590         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26591         
26592         //if(!this.el || !this.getEditor()) {
26593         //    this.value = value;
26594             //this.setValue.defer(100,this,[value]);    
26595         //    return;
26596         //} 
26597         
26598         if(!this.getEditor()) {
26599             return;
26600         }
26601         
26602         this.getEditor().SetData(value);
26603         
26604         //
26605
26606     },
26607
26608     /**
26609      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26610      * @return {Mixed} value The field value
26611      */
26612     getValue : function()
26613     {
26614         
26615         if (this.frame && this.frame.dom.style.display == 'none') {
26616             return Roo.form.FCKeditor.superclass.getValue.call(this);
26617         }
26618         
26619         if(!this.el || !this.getEditor()) {
26620            
26621            // this.getValue.defer(100,this); 
26622             return this.value;
26623         }
26624        
26625         
26626         var value=this.getEditor().GetData();
26627         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26628         return Roo.form.FCKeditor.superclass.getValue.call(this);
26629         
26630
26631     },
26632
26633     /**
26634      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26635      * @return {Mixed} value The field value
26636      */
26637     getRawValue : function()
26638     {
26639         if (this.frame && this.frame.dom.style.display == 'none') {
26640             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26641         }
26642         
26643         if(!this.el || !this.getEditor()) {
26644             //this.getRawValue.defer(100,this); 
26645             return this.value;
26646             return;
26647         }
26648         
26649         
26650         
26651         var value=this.getEditor().GetData();
26652         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26653         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26654          
26655     },
26656     
26657     setSize : function(w,h) {
26658         
26659         
26660         
26661         //if (this.frame && this.frame.dom.style.display == 'none') {
26662         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26663         //    return;
26664         //}
26665         //if(!this.el || !this.getEditor()) {
26666         //    this.setSize.defer(100,this, [w,h]); 
26667         //    return;
26668         //}
26669         
26670         
26671         
26672         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26673         
26674         this.frame.dom.setAttribute('width', w);
26675         this.frame.dom.setAttribute('height', h);
26676         this.frame.setSize(w,h);
26677         
26678     },
26679     
26680     toggleSourceEdit : function(value) {
26681         
26682       
26683          
26684         this.el.dom.style.display = value ? '' : 'none';
26685         this.frame.dom.style.display = value ?  'none' : '';
26686         
26687     },
26688     
26689     
26690     focus: function(tag)
26691     {
26692         if (this.frame.dom.style.display == 'none') {
26693             return Roo.form.FCKeditor.superclass.focus.call(this);
26694         }
26695         if(!this.el || !this.getEditor()) {
26696             this.focus.defer(100,this, [tag]); 
26697             return;
26698         }
26699         
26700         
26701         
26702         
26703         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26704         this.getEditor().Focus();
26705         if (tgs.length) {
26706             if (!this.getEditor().Selection.GetSelection()) {
26707                 this.focus.defer(100,this, [tag]); 
26708                 return;
26709             }
26710             
26711             
26712             var r = this.getEditor().EditorDocument.createRange();
26713             r.setStart(tgs[0],0);
26714             r.setEnd(tgs[0],0);
26715             this.getEditor().Selection.GetSelection().removeAllRanges();
26716             this.getEditor().Selection.GetSelection().addRange(r);
26717             this.getEditor().Focus();
26718         }
26719         
26720     },
26721     
26722     
26723     
26724     replaceTextarea : function()
26725     {
26726         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26727             return ;
26728         }
26729         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26730         //{
26731             // We must check the elements firstly using the Id and then the name.
26732         var oTextarea = document.getElementById( this.getId() );
26733         
26734         var colElementsByName = document.getElementsByName( this.getId() ) ;
26735          
26736         oTextarea.style.display = 'none' ;
26737
26738         if ( oTextarea.tabIndex ) {            
26739             this.TabIndex = oTextarea.tabIndex ;
26740         }
26741         
26742         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26743         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26744         this.frame = Roo.get(this.getId() + '___Frame')
26745     },
26746     
26747     _getConfigHtml : function()
26748     {
26749         var sConfig = '' ;
26750
26751         for ( var o in this.fckconfig ) {
26752             sConfig += sConfig.length > 0  ? '&amp;' : '';
26753             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26754         }
26755
26756         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26757     },
26758     
26759     
26760     _getIFrameHtml : function()
26761     {
26762         var sFile = 'fckeditor.html' ;
26763         /* no idea what this is about..
26764         try
26765         {
26766             if ( (/fcksource=true/i).test( window.top.location.search ) )
26767                 sFile = 'fckeditor.original.html' ;
26768         }
26769         catch (e) { 
26770         */
26771
26772         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26773         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26774         
26775         
26776         var html = '<iframe id="' + this.getId() +
26777             '___Frame" src="' + sLink +
26778             '" width="' + this.width +
26779             '" height="' + this.height + '"' +
26780             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26781             ' frameborder="0" scrolling="no"></iframe>' ;
26782
26783         return html ;
26784     },
26785     
26786     _insertHtmlBefore : function( html, element )
26787     {
26788         if ( element.insertAdjacentHTML )       {
26789             // IE
26790             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26791         } else { // Gecko
26792             var oRange = document.createRange() ;
26793             oRange.setStartBefore( element ) ;
26794             var oFragment = oRange.createContextualFragment( html );
26795             element.parentNode.insertBefore( oFragment, element ) ;
26796         }
26797     }
26798     
26799     
26800   
26801     
26802     
26803     
26804     
26805
26806 });
26807
26808 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26809
26810 function FCKeditor_OnComplete(editorInstance){
26811     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26812     f.fckEditor = editorInstance;
26813     //console.log("loaded");
26814     f.fireEvent('editorinit', f, editorInstance);
26815
26816   
26817
26818  
26819
26820
26821
26822
26823
26824
26825
26826
26827
26828
26829
26830
26831
26832
26833
26834 //<script type="text/javascript">
26835 /**
26836  * @class Roo.form.GridField
26837  * @extends Roo.form.Field
26838  * Embed a grid (or editable grid into a form)
26839  * STATUS ALPHA
26840  * 
26841  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26842  * it needs 
26843  * xgrid.store = Roo.data.Store
26844  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26845  * xgrid.store.reader = Roo.data.JsonReader 
26846  * 
26847  * 
26848  * @constructor
26849  * Creates a new GridField
26850  * @param {Object} config Configuration options
26851  */
26852 Roo.form.GridField = function(config){
26853     Roo.form.GridField.superclass.constructor.call(this, config);
26854      
26855 };
26856
26857 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26858     /**
26859      * @cfg {Number} width  - used to restrict width of grid..
26860      */
26861     width : 100,
26862     /**
26863      * @cfg {Number} height - used to restrict height of grid..
26864      */
26865     height : 50,
26866      /**
26867      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26868          * 
26869          *}
26870      */
26871     xgrid : false, 
26872     /**
26873      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26874      * {tag: "input", type: "checkbox", autocomplete: "off"})
26875      */
26876    // defaultAutoCreate : { tag: 'div' },
26877     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26878     /**
26879      * @cfg {String} addTitle Text to include for adding a title.
26880      */
26881     addTitle : false,
26882     //
26883     onResize : function(){
26884         Roo.form.Field.superclass.onResize.apply(this, arguments);
26885     },
26886
26887     initEvents : function(){
26888         // Roo.form.Checkbox.superclass.initEvents.call(this);
26889         // has no events...
26890        
26891     },
26892
26893
26894     getResizeEl : function(){
26895         return this.wrap;
26896     },
26897
26898     getPositionEl : function(){
26899         return this.wrap;
26900     },
26901
26902     // private
26903     onRender : function(ct, position){
26904         
26905         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26906         var style = this.style;
26907         delete this.style;
26908         
26909         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26910         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26911         this.viewEl = this.wrap.createChild({ tag: 'div' });
26912         if (style) {
26913             this.viewEl.applyStyles(style);
26914         }
26915         if (this.width) {
26916             this.viewEl.setWidth(this.width);
26917         }
26918         if (this.height) {
26919             this.viewEl.setHeight(this.height);
26920         }
26921         //if(this.inputValue !== undefined){
26922         //this.setValue(this.value);
26923         
26924         
26925         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26926         
26927         
26928         this.grid.render();
26929         this.grid.getDataSource().on('remove', this.refreshValue, this);
26930         this.grid.getDataSource().on('update', this.refreshValue, this);
26931         this.grid.on('afteredit', this.refreshValue, this);
26932  
26933     },
26934      
26935     
26936     /**
26937      * Sets the value of the item. 
26938      * @param {String} either an object  or a string..
26939      */
26940     setValue : function(v){
26941         //this.value = v;
26942         v = v || []; // empty set..
26943         // this does not seem smart - it really only affects memoryproxy grids..
26944         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26945             var ds = this.grid.getDataSource();
26946             // assumes a json reader..
26947             var data = {}
26948             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
26949             ds.loadData( data);
26950         }
26951         // clear selection so it does not get stale.
26952         if (this.grid.sm) { 
26953             this.grid.sm.clearSelections();
26954         }
26955         
26956         Roo.form.GridField.superclass.setValue.call(this, v);
26957         this.refreshValue();
26958         // should load data in the grid really....
26959     },
26960     
26961     // private
26962     refreshValue: function() {
26963          var val = [];
26964         this.grid.getDataSource().each(function(r) {
26965             val.push(r.data);
26966         });
26967         this.el.dom.value = Roo.encode(val);
26968     }
26969     
26970      
26971     
26972     
26973 });/*
26974  * Based on:
26975  * Ext JS Library 1.1.1
26976  * Copyright(c) 2006-2007, Ext JS, LLC.
26977  *
26978  * Originally Released Under LGPL - original licence link has changed is not relivant.
26979  *
26980  * Fork - LGPL
26981  * <script type="text/javascript">
26982  */
26983 /**
26984  * @class Roo.form.DisplayField
26985  * @extends Roo.form.Field
26986  * A generic Field to display non-editable data.
26987  * @cfg {Boolean} closable (true|false) default false
26988  * @constructor
26989  * Creates a new Display Field item.
26990  * @param {Object} config Configuration options
26991  */
26992 Roo.form.DisplayField = function(config){
26993     Roo.form.DisplayField.superclass.constructor.call(this, config);
26994     
26995     this.addEvents({
26996         /**
26997          * @event close
26998          * Fires after the click the close btn
26999              * @param {Roo.form.DisplayField} this
27000              */
27001         close : true
27002     });
27003 };
27004
27005 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
27006     inputType:      'hidden',
27007     allowBlank:     true,
27008     readOnly:         true,
27009     
27010  
27011     /**
27012      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27013      */
27014     focusClass : undefined,
27015     /**
27016      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27017      */
27018     fieldClass: 'x-form-field',
27019     
27020      /**
27021      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27022      */
27023     valueRenderer: undefined,
27024     
27025     width: 100,
27026     /**
27027      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27028      * {tag: "input", type: "checkbox", autocomplete: "off"})
27029      */
27030      
27031  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27032  
27033     closable : false,
27034     
27035     onResize : function(){
27036         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27037         
27038     },
27039
27040     initEvents : function(){
27041         // Roo.form.Checkbox.superclass.initEvents.call(this);
27042         // has no events...
27043         
27044         if(this.closable){
27045             this.closeEl.on('click', this.onClose, this);
27046         }
27047        
27048     },
27049
27050
27051     getResizeEl : function(){
27052         return this.wrap;
27053     },
27054
27055     getPositionEl : function(){
27056         return this.wrap;
27057     },
27058
27059     // private
27060     onRender : function(ct, position){
27061         
27062         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27063         //if(this.inputValue !== undefined){
27064         this.wrap = this.el.wrap();
27065         
27066         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27067         
27068         if(this.closable){
27069             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27070         }
27071         
27072         if (this.bodyStyle) {
27073             this.viewEl.applyStyles(this.bodyStyle);
27074         }
27075         //this.viewEl.setStyle('padding', '2px');
27076         
27077         this.setValue(this.value);
27078         
27079     },
27080 /*
27081     // private
27082     initValue : Roo.emptyFn,
27083
27084   */
27085
27086         // private
27087     onClick : function(){
27088         
27089     },
27090
27091     /**
27092      * Sets the checked state of the checkbox.
27093      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27094      */
27095     setValue : function(v){
27096         this.value = v;
27097         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27098         // this might be called before we have a dom element..
27099         if (!this.viewEl) {
27100             return;
27101         }
27102         this.viewEl.dom.innerHTML = html;
27103         Roo.form.DisplayField.superclass.setValue.call(this, v);
27104
27105     },
27106     
27107     onClose : function(e)
27108     {
27109         e.preventDefault();
27110         
27111         this.fireEvent('close', this);
27112     }
27113 });/*
27114  * 
27115  * Licence- LGPL
27116  * 
27117  */
27118
27119 /**
27120  * @class Roo.form.DayPicker
27121  * @extends Roo.form.Field
27122  * A Day picker show [M] [T] [W] ....
27123  * @constructor
27124  * Creates a new Day Picker
27125  * @param {Object} config Configuration options
27126  */
27127 Roo.form.DayPicker= function(config){
27128     Roo.form.DayPicker.superclass.constructor.call(this, config);
27129      
27130 };
27131
27132 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27133     /**
27134      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27135      */
27136     focusClass : undefined,
27137     /**
27138      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27139      */
27140     fieldClass: "x-form-field",
27141    
27142     /**
27143      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27144      * {tag: "input", type: "checkbox", autocomplete: "off"})
27145      */
27146     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27147     
27148    
27149     actionMode : 'viewEl', 
27150     //
27151     // private
27152  
27153     inputType : 'hidden',
27154     
27155      
27156     inputElement: false, // real input element?
27157     basedOn: false, // ????
27158     
27159     isFormField: true, // not sure where this is needed!!!!
27160
27161     onResize : function(){
27162         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27163         if(!this.boxLabel){
27164             this.el.alignTo(this.wrap, 'c-c');
27165         }
27166     },
27167
27168     initEvents : function(){
27169         Roo.form.Checkbox.superclass.initEvents.call(this);
27170         this.el.on("click", this.onClick,  this);
27171         this.el.on("change", this.onClick,  this);
27172     },
27173
27174
27175     getResizeEl : function(){
27176         return this.wrap;
27177     },
27178
27179     getPositionEl : function(){
27180         return this.wrap;
27181     },
27182
27183     
27184     // private
27185     onRender : function(ct, position){
27186         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27187        
27188         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27189         
27190         var r1 = '<table><tr>';
27191         var r2 = '<tr class="x-form-daypick-icons">';
27192         for (var i=0; i < 7; i++) {
27193             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27194             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27195         }
27196         
27197         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27198         viewEl.select('img').on('click', this.onClick, this);
27199         this.viewEl = viewEl;   
27200         
27201         
27202         // this will not work on Chrome!!!
27203         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27204         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27205         
27206         
27207           
27208
27209     },
27210
27211     // private
27212     initValue : Roo.emptyFn,
27213
27214     /**
27215      * Returns the checked state of the checkbox.
27216      * @return {Boolean} True if checked, else false
27217      */
27218     getValue : function(){
27219         return this.el.dom.value;
27220         
27221     },
27222
27223         // private
27224     onClick : function(e){ 
27225         //this.setChecked(!this.checked);
27226         Roo.get(e.target).toggleClass('x-menu-item-checked');
27227         this.refreshValue();
27228         //if(this.el.dom.checked != this.checked){
27229         //    this.setValue(this.el.dom.checked);
27230        // }
27231     },
27232     
27233     // private
27234     refreshValue : function()
27235     {
27236         var val = '';
27237         this.viewEl.select('img',true).each(function(e,i,n)  {
27238             val += e.is(".x-menu-item-checked") ? String(n) : '';
27239         });
27240         this.setValue(val, true);
27241     },
27242
27243     /**
27244      * Sets the checked state of the checkbox.
27245      * On is always based on a string comparison between inputValue and the param.
27246      * @param {Boolean/String} value - the value to set 
27247      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27248      */
27249     setValue : function(v,suppressEvent){
27250         if (!this.el.dom) {
27251             return;
27252         }
27253         var old = this.el.dom.value ;
27254         this.el.dom.value = v;
27255         if (suppressEvent) {
27256             return ;
27257         }
27258          
27259         // update display..
27260         this.viewEl.select('img',true).each(function(e,i,n)  {
27261             
27262             var on = e.is(".x-menu-item-checked");
27263             var newv = v.indexOf(String(n)) > -1;
27264             if (on != newv) {
27265                 e.toggleClass('x-menu-item-checked');
27266             }
27267             
27268         });
27269         
27270         
27271         this.fireEvent('change', this, v, old);
27272         
27273         
27274     },
27275    
27276     // handle setting of hidden value by some other method!!?!?
27277     setFromHidden: function()
27278     {
27279         if(!this.el){
27280             return;
27281         }
27282         //console.log("SET FROM HIDDEN");
27283         //alert('setFrom hidden');
27284         this.setValue(this.el.dom.value);
27285     },
27286     
27287     onDestroy : function()
27288     {
27289         if(this.viewEl){
27290             Roo.get(this.viewEl).remove();
27291         }
27292          
27293         Roo.form.DayPicker.superclass.onDestroy.call(this);
27294     }
27295
27296 });/*
27297  * RooJS Library 1.1.1
27298  * Copyright(c) 2008-2011  Alan Knowles
27299  *
27300  * License - LGPL
27301  */
27302  
27303
27304 /**
27305  * @class Roo.form.ComboCheck
27306  * @extends Roo.form.ComboBox
27307  * A combobox for multiple select items.
27308  *
27309  * FIXME - could do with a reset button..
27310  * 
27311  * @constructor
27312  * Create a new ComboCheck
27313  * @param {Object} config Configuration options
27314  */
27315 Roo.form.ComboCheck = function(config){
27316     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27317     // should verify some data...
27318     // like
27319     // hiddenName = required..
27320     // displayField = required
27321     // valudField == required
27322     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27323     var _t = this;
27324     Roo.each(req, function(e) {
27325         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27326             throw "Roo.form.ComboCheck : missing value for: " + e;
27327         }
27328     });
27329     
27330     
27331 };
27332
27333 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27334      
27335      
27336     editable : false,
27337      
27338     selectedClass: 'x-menu-item-checked', 
27339     
27340     // private
27341     onRender : function(ct, position){
27342         var _t = this;
27343         
27344         
27345         
27346         if(!this.tpl){
27347             var cls = 'x-combo-list';
27348
27349             
27350             this.tpl =  new Roo.Template({
27351                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27352                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27353                    '<span>{' + this.displayField + '}</span>' +
27354                     '</div>' 
27355                 
27356             });
27357         }
27358  
27359         
27360         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27361         this.view.singleSelect = false;
27362         this.view.multiSelect = true;
27363         this.view.toggleSelect = true;
27364         this.pageTb.add(new Roo.Toolbar.Fill(), {
27365             
27366             text: 'Done',
27367             handler: function()
27368             {
27369                 _t.collapse();
27370             }
27371         });
27372     },
27373     
27374     onViewOver : function(e, t){
27375         // do nothing...
27376         return;
27377         
27378     },
27379     
27380     onViewClick : function(doFocus,index){
27381         return;
27382         
27383     },
27384     select: function () {
27385         //Roo.log("SELECT CALLED");
27386     },
27387      
27388     selectByValue : function(xv, scrollIntoView){
27389         var ar = this.getValueArray();
27390         var sels = [];
27391         
27392         Roo.each(ar, function(v) {
27393             if(v === undefined || v === null){
27394                 return;
27395             }
27396             var r = this.findRecord(this.valueField, v);
27397             if(r){
27398                 sels.push(this.store.indexOf(r))
27399                 
27400             }
27401         },this);
27402         this.view.select(sels);
27403         return false;
27404     },
27405     
27406     
27407     
27408     onSelect : function(record, index){
27409        // Roo.log("onselect Called");
27410        // this is only called by the clear button now..
27411         this.view.clearSelections();
27412         this.setValue('[]');
27413         if (this.value != this.valueBefore) {
27414             this.fireEvent('change', this, this.value, this.valueBefore);
27415             this.valueBefore = this.value;
27416         }
27417     },
27418     getValueArray : function()
27419     {
27420         var ar = [] ;
27421         
27422         try {
27423             //Roo.log(this.value);
27424             if (typeof(this.value) == 'undefined') {
27425                 return [];
27426             }
27427             var ar = Roo.decode(this.value);
27428             return  ar instanceof Array ? ar : []; //?? valid?
27429             
27430         } catch(e) {
27431             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27432             return [];
27433         }
27434          
27435     },
27436     expand : function ()
27437     {
27438         
27439         Roo.form.ComboCheck.superclass.expand.call(this);
27440         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27441         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27442         
27443
27444     },
27445     
27446     collapse : function(){
27447         Roo.form.ComboCheck.superclass.collapse.call(this);
27448         var sl = this.view.getSelectedIndexes();
27449         var st = this.store;
27450         var nv = [];
27451         var tv = [];
27452         var r;
27453         Roo.each(sl, function(i) {
27454             r = st.getAt(i);
27455             nv.push(r.get(this.valueField));
27456         },this);
27457         this.setValue(Roo.encode(nv));
27458         if (this.value != this.valueBefore) {
27459
27460             this.fireEvent('change', this, this.value, this.valueBefore);
27461             this.valueBefore = this.value;
27462         }
27463         
27464     },
27465     
27466     setValue : function(v){
27467         // Roo.log(v);
27468         this.value = v;
27469         
27470         var vals = this.getValueArray();
27471         var tv = [];
27472         Roo.each(vals, function(k) {
27473             var r = this.findRecord(this.valueField, k);
27474             if(r){
27475                 tv.push(r.data[this.displayField]);
27476             }else if(this.valueNotFoundText !== undefined){
27477                 tv.push( this.valueNotFoundText );
27478             }
27479         },this);
27480        // Roo.log(tv);
27481         
27482         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27483         this.hiddenField.value = v;
27484         this.value = v;
27485     }
27486     
27487 });/*
27488  * Based on:
27489  * Ext JS Library 1.1.1
27490  * Copyright(c) 2006-2007, Ext JS, LLC.
27491  *
27492  * Originally Released Under LGPL - original licence link has changed is not relivant.
27493  *
27494  * Fork - LGPL
27495  * <script type="text/javascript">
27496  */
27497  
27498 /**
27499  * @class Roo.form.Signature
27500  * @extends Roo.form.Field
27501  * Signature field.  
27502  * @constructor
27503  * 
27504  * @param {Object} config Configuration options
27505  */
27506
27507 Roo.form.Signature = function(config){
27508     Roo.form.Signature.superclass.constructor.call(this, config);
27509     
27510     this.addEvents({// not in used??
27511          /**
27512          * @event confirm
27513          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27514              * @param {Roo.form.Signature} combo This combo box
27515              */
27516         'confirm' : true,
27517         /**
27518          * @event reset
27519          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27520              * @param {Roo.form.ComboBox} combo This combo box
27521              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27522              */
27523         'reset' : true
27524     });
27525 };
27526
27527 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27528     /**
27529      * @cfg {Object} labels Label to use when rendering a form.
27530      * defaults to 
27531      * labels : { 
27532      *      clear : "Clear",
27533      *      confirm : "Confirm"
27534      *  }
27535      */
27536     labels : { 
27537         clear : "Clear",
27538         confirm : "Confirm"
27539     },
27540     /**
27541      * @cfg {Number} width The signature panel width (defaults to 300)
27542      */
27543     width: 300,
27544     /**
27545      * @cfg {Number} height The signature panel height (defaults to 100)
27546      */
27547     height : 100,
27548     /**
27549      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27550      */
27551     allowBlank : false,
27552     
27553     //private
27554     // {Object} signPanel The signature SVG panel element (defaults to {})
27555     signPanel : {},
27556     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27557     isMouseDown : false,
27558     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27559     isConfirmed : false,
27560     // {String} signatureTmp SVG mapping string (defaults to empty string)
27561     signatureTmp : '',
27562     
27563     
27564     defaultAutoCreate : { // modified by initCompnoent..
27565         tag: "input",
27566         type:"hidden"
27567     },
27568
27569     // private
27570     onRender : function(ct, position){
27571         
27572         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27573         
27574         this.wrap = this.el.wrap({
27575             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27576         });
27577         
27578         this.createToolbar(this);
27579         this.signPanel = this.wrap.createChild({
27580                 tag: 'div',
27581                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27582             }, this.el
27583         );
27584             
27585         this.svgID = Roo.id();
27586         this.svgEl = this.signPanel.createChild({
27587               xmlns : 'http://www.w3.org/2000/svg',
27588               tag : 'svg',
27589               id : this.svgID + "-svg",
27590               width: this.width,
27591               height: this.height,
27592               viewBox: '0 0 '+this.width+' '+this.height,
27593               cn : [
27594                 {
27595                     tag: "rect",
27596                     id: this.svgID + "-svg-r",
27597                     width: this.width,
27598                     height: this.height,
27599                     fill: "#ffa"
27600                 },
27601                 {
27602                     tag: "line",
27603                     id: this.svgID + "-svg-l",
27604                     x1: "0", // start
27605                     y1: (this.height*0.8), // start set the line in 80% of height
27606                     x2: this.width, // end
27607                     y2: (this.height*0.8), // end set the line in 80% of height
27608                     'stroke': "#666",
27609                     'stroke-width': "1",
27610                     'stroke-dasharray': "3",
27611                     'shape-rendering': "crispEdges",
27612                     'pointer-events': "none"
27613                 },
27614                 {
27615                     tag: "path",
27616                     id: this.svgID + "-svg-p",
27617                     'stroke': "navy",
27618                     'stroke-width': "3",
27619                     'fill': "none",
27620                     'pointer-events': 'none'
27621                 }
27622               ]
27623         });
27624         this.createSVG();
27625         this.svgBox = this.svgEl.dom.getScreenCTM();
27626     },
27627     createSVG : function(){ 
27628         var svg = this.signPanel;
27629         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27630         var t = this;
27631
27632         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27633         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27634         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27635         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27636         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27637         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27638         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27639         
27640     },
27641     isTouchEvent : function(e){
27642         return e.type.match(/^touch/);
27643     },
27644     getCoords : function (e) {
27645         var pt    = this.svgEl.dom.createSVGPoint();
27646         pt.x = e.clientX; 
27647         pt.y = e.clientY;
27648         if (this.isTouchEvent(e)) {
27649             pt.x =  e.targetTouches[0].clientX;
27650             pt.y = e.targetTouches[0].clientY;
27651         }
27652         var a = this.svgEl.dom.getScreenCTM();
27653         var b = a.inverse();
27654         var mx = pt.matrixTransform(b);
27655         return mx.x + ',' + mx.y;
27656     },
27657     //mouse event headler 
27658     down : function (e) {
27659         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27660         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27661         
27662         this.isMouseDown = true;
27663         
27664         e.preventDefault();
27665     },
27666     move : function (e) {
27667         if (this.isMouseDown) {
27668             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27669             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27670         }
27671         
27672         e.preventDefault();
27673     },
27674     up : function (e) {
27675         this.isMouseDown = false;
27676         var sp = this.signatureTmp.split(' ');
27677         
27678         if(sp.length > 1){
27679             if(!sp[sp.length-2].match(/^L/)){
27680                 sp.pop();
27681                 sp.pop();
27682                 sp.push("");
27683                 this.signatureTmp = sp.join(" ");
27684             }
27685         }
27686         if(this.getValue() != this.signatureTmp){
27687             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27688             this.isConfirmed = false;
27689         }
27690         e.preventDefault();
27691     },
27692     
27693     /**
27694      * Protected method that will not generally be called directly. It
27695      * is called when the editor creates its toolbar. Override this method if you need to
27696      * add custom toolbar buttons.
27697      * @param {HtmlEditor} editor
27698      */
27699     createToolbar : function(editor){
27700          function btn(id, toggle, handler){
27701             var xid = fid + '-'+ id ;
27702             return {
27703                 id : xid,
27704                 cmd : id,
27705                 cls : 'x-btn-icon x-edit-'+id,
27706                 enableToggle:toggle !== false,
27707                 scope: editor, // was editor...
27708                 handler:handler||editor.relayBtnCmd,
27709                 clickEvent:'mousedown',
27710                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27711                 tabIndex:-1
27712             };
27713         }
27714         
27715         
27716         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27717         this.tb = tb;
27718         this.tb.add(
27719            {
27720                 cls : ' x-signature-btn x-signature-'+id,
27721                 scope: editor, // was editor...
27722                 handler: this.reset,
27723                 clickEvent:'mousedown',
27724                 text: this.labels.clear
27725             },
27726             {
27727                  xtype : 'Fill',
27728                  xns: Roo.Toolbar
27729             }, 
27730             {
27731                 cls : '  x-signature-btn x-signature-'+id,
27732                 scope: editor, // was editor...
27733                 handler: this.confirmHandler,
27734                 clickEvent:'mousedown',
27735                 text: this.labels.confirm
27736             }
27737         );
27738     
27739     },
27740     //public
27741     /**
27742      * when user is clicked confirm then show this image.....
27743      * 
27744      * @return {String} Image Data URI
27745      */
27746     getImageDataURI : function(){
27747         var svg = this.svgEl.dom.parentNode.innerHTML;
27748         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27749         return src; 
27750     },
27751     /**
27752      * 
27753      * @return {Boolean} this.isConfirmed
27754      */
27755     getConfirmed : function(){
27756         return this.isConfirmed;
27757     },
27758     /**
27759      * 
27760      * @return {Number} this.width
27761      */
27762     getWidth : function(){
27763         return this.width;
27764     },
27765     /**
27766      * 
27767      * @return {Number} this.height
27768      */
27769     getHeight : function(){
27770         return this.height;
27771     },
27772     // private
27773     getSignature : function(){
27774         return this.signatureTmp;
27775     },
27776     // private
27777     reset : function(){
27778         this.signatureTmp = '';
27779         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27780         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27781         this.isConfirmed = false;
27782         Roo.form.Signature.superclass.reset.call(this);
27783     },
27784     setSignature : function(s){
27785         this.signatureTmp = s;
27786         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27787         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27788         this.setValue(s);
27789         this.isConfirmed = false;
27790         Roo.form.Signature.superclass.reset.call(this);
27791     }, 
27792     test : function(){
27793 //        Roo.log(this.signPanel.dom.contentWindow.up())
27794     },
27795     //private
27796     setConfirmed : function(){
27797         
27798         
27799         
27800 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27801     },
27802     // private
27803     confirmHandler : function(){
27804         if(!this.getSignature()){
27805             return;
27806         }
27807         
27808         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27809         this.setValue(this.getSignature());
27810         this.isConfirmed = true;
27811         
27812         this.fireEvent('confirm', this);
27813     },
27814     // private
27815     // Subclasses should provide the validation implementation by overriding this
27816     validateValue : function(value){
27817         if(this.allowBlank){
27818             return true;
27819         }
27820         
27821         if(this.isConfirmed){
27822             return true;
27823         }
27824         return false;
27825     }
27826 });/*
27827  * Based on:
27828  * Ext JS Library 1.1.1
27829  * Copyright(c) 2006-2007, Ext JS, LLC.
27830  *
27831  * Originally Released Under LGPL - original licence link has changed is not relivant.
27832  *
27833  * Fork - LGPL
27834  * <script type="text/javascript">
27835  */
27836  
27837
27838 /**
27839  * @class Roo.form.ComboBox
27840  * @extends Roo.form.TriggerField
27841  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27842  * @constructor
27843  * Create a new ComboBox.
27844  * @param {Object} config Configuration options
27845  */
27846 Roo.form.Select = function(config){
27847     Roo.form.Select.superclass.constructor.call(this, config);
27848      
27849 };
27850
27851 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27852     /**
27853      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27854      */
27855     /**
27856      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27857      * rendering into an Roo.Editor, defaults to false)
27858      */
27859     /**
27860      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27861      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27862      */
27863     /**
27864      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27865      */
27866     /**
27867      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27868      * the dropdown list (defaults to undefined, with no header element)
27869      */
27870
27871      /**
27872      * @cfg {String/Roo.Template} tpl The template to use to render the output
27873      */
27874      
27875     // private
27876     defaultAutoCreate : {tag: "select"  },
27877     /**
27878      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27879      */
27880     listWidth: undefined,
27881     /**
27882      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27883      * mode = 'remote' or 'text' if mode = 'local')
27884      */
27885     displayField: undefined,
27886     /**
27887      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27888      * mode = 'remote' or 'value' if mode = 'local'). 
27889      * Note: use of a valueField requires the user make a selection
27890      * in order for a value to be mapped.
27891      */
27892     valueField: undefined,
27893     
27894     
27895     /**
27896      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27897      * field's data value (defaults to the underlying DOM element's name)
27898      */
27899     hiddenName: undefined,
27900     /**
27901      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27902      */
27903     listClass: '',
27904     /**
27905      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27906      */
27907     selectedClass: 'x-combo-selected',
27908     /**
27909      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27910      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27911      * which displays a downward arrow icon).
27912      */
27913     triggerClass : 'x-form-arrow-trigger',
27914     /**
27915      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27916      */
27917     shadow:'sides',
27918     /**
27919      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27920      * anchor positions (defaults to 'tl-bl')
27921      */
27922     listAlign: 'tl-bl?',
27923     /**
27924      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27925      */
27926     maxHeight: 300,
27927     /**
27928      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27929      * query specified by the allQuery config option (defaults to 'query')
27930      */
27931     triggerAction: 'query',
27932     /**
27933      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27934      * (defaults to 4, does not apply if editable = false)
27935      */
27936     minChars : 4,
27937     /**
27938      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27939      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27940      */
27941     typeAhead: false,
27942     /**
27943      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27944      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27945      */
27946     queryDelay: 500,
27947     /**
27948      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27949      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
27950      */
27951     pageSize: 0,
27952     /**
27953      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
27954      * when editable = true (defaults to false)
27955      */
27956     selectOnFocus:false,
27957     /**
27958      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27959      */
27960     queryParam: 'query',
27961     /**
27962      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
27963      * when mode = 'remote' (defaults to 'Loading...')
27964      */
27965     loadingText: 'Loading...',
27966     /**
27967      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27968      */
27969     resizable: false,
27970     /**
27971      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27972      */
27973     handleHeight : 8,
27974     /**
27975      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27976      * traditional select (defaults to true)
27977      */
27978     editable: true,
27979     /**
27980      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27981      */
27982     allQuery: '',
27983     /**
27984      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27985      */
27986     mode: 'remote',
27987     /**
27988      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27989      * listWidth has a higher value)
27990      */
27991     minListWidth : 70,
27992     /**
27993      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
27994      * allow the user to set arbitrary text into the field (defaults to false)
27995      */
27996     forceSelection:false,
27997     /**
27998      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
27999      * if typeAhead = true (defaults to 250)
28000      */
28001     typeAheadDelay : 250,
28002     /**
28003      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28004      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28005      */
28006     valueNotFoundText : undefined,
28007     
28008     /**
28009      * @cfg {String} defaultValue The value displayed after loading the store.
28010      */
28011     defaultValue: '',
28012     
28013     /**
28014      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28015      */
28016     blockFocus : false,
28017     
28018     /**
28019      * @cfg {Boolean} disableClear Disable showing of clear button.
28020      */
28021     disableClear : false,
28022     /**
28023      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
28024      */
28025     alwaysQuery : false,
28026     
28027     //private
28028     addicon : false,
28029     editicon: false,
28030     
28031     // element that contains real text value.. (when hidden is used..)
28032      
28033     // private
28034     onRender : function(ct, position){
28035         Roo.form.Field.prototype.onRender.call(this, ct, position);
28036         
28037         if(this.store){
28038             this.store.on('beforeload', this.onBeforeLoad, this);
28039             this.store.on('load', this.onLoad, this);
28040             this.store.on('loadexception', this.onLoadException, this);
28041             this.store.load({});
28042         }
28043         
28044         
28045         
28046     },
28047
28048     // private
28049     initEvents : function(){
28050         //Roo.form.ComboBox.superclass.initEvents.call(this);
28051  
28052     },
28053
28054     onDestroy : function(){
28055        
28056         if(this.store){
28057             this.store.un('beforeload', this.onBeforeLoad, this);
28058             this.store.un('load', this.onLoad, this);
28059             this.store.un('loadexception', this.onLoadException, this);
28060         }
28061         //Roo.form.ComboBox.superclass.onDestroy.call(this);
28062     },
28063
28064     // private
28065     fireKey : function(e){
28066         if(e.isNavKeyPress() && !this.list.isVisible()){
28067             this.fireEvent("specialkey", this, e);
28068         }
28069     },
28070
28071     // private
28072     onResize: function(w, h){
28073         
28074         return; 
28075     
28076         
28077     },
28078
28079     /**
28080      * Allow or prevent the user from directly editing the field text.  If false is passed,
28081      * the user will only be able to select from the items defined in the dropdown list.  This method
28082      * is the runtime equivalent of setting the 'editable' config option at config time.
28083      * @param {Boolean} value True to allow the user to directly edit the field text
28084      */
28085     setEditable : function(value){
28086          
28087     },
28088
28089     // private
28090     onBeforeLoad : function(){
28091         
28092         Roo.log("Select before load");
28093         return;
28094     
28095         this.innerList.update(this.loadingText ?
28096                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28097         //this.restrictHeight();
28098         this.selectedIndex = -1;
28099     },
28100
28101     // private
28102     onLoad : function(){
28103
28104     
28105         var dom = this.el.dom;
28106         dom.innerHTML = '';
28107          var od = dom.ownerDocument;
28108          
28109         if (this.emptyText) {
28110             var op = od.createElement('option');
28111             op.setAttribute('value', '');
28112             op.innerHTML = String.format('{0}', this.emptyText);
28113             dom.appendChild(op);
28114         }
28115         if(this.store.getCount() > 0){
28116            
28117             var vf = this.valueField;
28118             var df = this.displayField;
28119             this.store.data.each(function(r) {
28120                 // which colmsn to use... testing - cdoe / title..
28121                 var op = od.createElement('option');
28122                 op.setAttribute('value', r.data[vf]);
28123                 op.innerHTML = String.format('{0}', r.data[df]);
28124                 dom.appendChild(op);
28125             });
28126             if (typeof(this.defaultValue != 'undefined')) {
28127                 this.setValue(this.defaultValue);
28128             }
28129             
28130              
28131         }else{
28132             //this.onEmptyResults();
28133         }
28134         //this.el.focus();
28135     },
28136     // private
28137     onLoadException : function()
28138     {
28139         dom.innerHTML = '';
28140             
28141         Roo.log("Select on load exception");
28142         return;
28143     
28144         this.collapse();
28145         Roo.log(this.store.reader.jsonData);
28146         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28147             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28148         }
28149         
28150         
28151     },
28152     // private
28153     onTypeAhead : function(){
28154          
28155     },
28156
28157     // private
28158     onSelect : function(record, index){
28159         Roo.log('on select?');
28160         return;
28161         if(this.fireEvent('beforeselect', this, record, index) !== false){
28162             this.setFromData(index > -1 ? record.data : false);
28163             this.collapse();
28164             this.fireEvent('select', this, record, index);
28165         }
28166     },
28167
28168     /**
28169      * Returns the currently selected field value or empty string if no value is set.
28170      * @return {String} value The selected value
28171      */
28172     getValue : function(){
28173         var dom = this.el.dom;
28174         this.value = dom.options[dom.selectedIndex].value;
28175         return this.value;
28176         
28177     },
28178
28179     /**
28180      * Clears any text/value currently set in the field
28181      */
28182     clearValue : function(){
28183         this.value = '';
28184         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28185         
28186     },
28187
28188     /**
28189      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28190      * will be displayed in the field.  If the value does not match the data value of an existing item,
28191      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28192      * Otherwise the field will be blank (although the value will still be set).
28193      * @param {String} value The value to match
28194      */
28195     setValue : function(v){
28196         var d = this.el.dom;
28197         for (var i =0; i < d.options.length;i++) {
28198             if (v == d.options[i].value) {
28199                 d.selectedIndex = i;
28200                 this.value = v;
28201                 return;
28202             }
28203         }
28204         this.clearValue();
28205     },
28206     /**
28207      * @property {Object} the last set data for the element
28208      */
28209     
28210     lastData : false,
28211     /**
28212      * Sets the value of the field based on a object which is related to the record format for the store.
28213      * @param {Object} value the value to set as. or false on reset?
28214      */
28215     setFromData : function(o){
28216         Roo.log('setfrom data?');
28217          
28218         
28219         
28220     },
28221     // private
28222     reset : function(){
28223         this.clearValue();
28224     },
28225     // private
28226     findRecord : function(prop, value){
28227         
28228         return false;
28229     
28230         var record;
28231         if(this.store.getCount() > 0){
28232             this.store.each(function(r){
28233                 if(r.data[prop] == value){
28234                     record = r;
28235                     return false;
28236                 }
28237                 return true;
28238             });
28239         }
28240         return record;
28241     },
28242     
28243     getName: function()
28244     {
28245         // returns hidden if it's set..
28246         if (!this.rendered) {return ''};
28247         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28248         
28249     },
28250      
28251
28252     
28253
28254     // private
28255     onEmptyResults : function(){
28256         Roo.log('empty results');
28257         //this.collapse();
28258     },
28259
28260     /**
28261      * Returns true if the dropdown list is expanded, else false.
28262      */
28263     isExpanded : function(){
28264         return false;
28265     },
28266
28267     /**
28268      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28269      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28270      * @param {String} value The data value of the item to select
28271      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28272      * selected item if it is not currently in view (defaults to true)
28273      * @return {Boolean} True if the value matched an item in the list, else false
28274      */
28275     selectByValue : function(v, scrollIntoView){
28276         Roo.log('select By Value');
28277         return false;
28278     
28279         if(v !== undefined && v !== null){
28280             var r = this.findRecord(this.valueField || this.displayField, v);
28281             if(r){
28282                 this.select(this.store.indexOf(r), scrollIntoView);
28283                 return true;
28284             }
28285         }
28286         return false;
28287     },
28288
28289     /**
28290      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28291      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28292      * @param {Number} index The zero-based index of the list item to select
28293      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28294      * selected item if it is not currently in view (defaults to true)
28295      */
28296     select : function(index, scrollIntoView){
28297         Roo.log('select ');
28298         return  ;
28299         
28300         this.selectedIndex = index;
28301         this.view.select(index);
28302         if(scrollIntoView !== false){
28303             var el = this.view.getNode(index);
28304             if(el){
28305                 this.innerList.scrollChildIntoView(el, false);
28306             }
28307         }
28308     },
28309
28310       
28311
28312     // private
28313     validateBlur : function(){
28314         
28315         return;
28316         
28317     },
28318
28319     // private
28320     initQuery : function(){
28321         this.doQuery(this.getRawValue());
28322     },
28323
28324     // private
28325     doForce : function(){
28326         if(this.el.dom.value.length > 0){
28327             this.el.dom.value =
28328                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28329              
28330         }
28331     },
28332
28333     /**
28334      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28335      * query allowing the query action to be canceled if needed.
28336      * @param {String} query The SQL query to execute
28337      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28338      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28339      * saved in the current store (defaults to false)
28340      */
28341     doQuery : function(q, forceAll){
28342         
28343         Roo.log('doQuery?');
28344         if(q === undefined || q === null){
28345             q = '';
28346         }
28347         var qe = {
28348             query: q,
28349             forceAll: forceAll,
28350             combo: this,
28351             cancel:false
28352         };
28353         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28354             return false;
28355         }
28356         q = qe.query;
28357         forceAll = qe.forceAll;
28358         if(forceAll === true || (q.length >= this.minChars)){
28359             if(this.lastQuery != q || this.alwaysQuery){
28360                 this.lastQuery = q;
28361                 if(this.mode == 'local'){
28362                     this.selectedIndex = -1;
28363                     if(forceAll){
28364                         this.store.clearFilter();
28365                     }else{
28366                         this.store.filter(this.displayField, q);
28367                     }
28368                     this.onLoad();
28369                 }else{
28370                     this.store.baseParams[this.queryParam] = q;
28371                     this.store.load({
28372                         params: this.getParams(q)
28373                     });
28374                     this.expand();
28375                 }
28376             }else{
28377                 this.selectedIndex = -1;
28378                 this.onLoad();   
28379             }
28380         }
28381     },
28382
28383     // private
28384     getParams : function(q){
28385         var p = {};
28386         //p[this.queryParam] = q;
28387         if(this.pageSize){
28388             p.start = 0;
28389             p.limit = this.pageSize;
28390         }
28391         return p;
28392     },
28393
28394     /**
28395      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28396      */
28397     collapse : function(){
28398         
28399     },
28400
28401     // private
28402     collapseIf : function(e){
28403         
28404     },
28405
28406     /**
28407      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28408      */
28409     expand : function(){
28410         
28411     } ,
28412
28413     // private
28414      
28415
28416     /** 
28417     * @cfg {Boolean} grow 
28418     * @hide 
28419     */
28420     /** 
28421     * @cfg {Number} growMin 
28422     * @hide 
28423     */
28424     /** 
28425     * @cfg {Number} growMax 
28426     * @hide 
28427     */
28428     /**
28429      * @hide
28430      * @method autoSize
28431      */
28432     
28433     setWidth : function()
28434     {
28435         
28436     },
28437     getResizeEl : function(){
28438         return this.el;
28439     }
28440 });//<script type="text/javasscript">
28441  
28442
28443 /**
28444  * @class Roo.DDView
28445  * A DnD enabled version of Roo.View.
28446  * @param {Element/String} container The Element in which to create the View.
28447  * @param {String} tpl The template string used to create the markup for each element of the View
28448  * @param {Object} config The configuration properties. These include all the config options of
28449  * {@link Roo.View} plus some specific to this class.<br>
28450  * <p>
28451  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28452  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28453  * <p>
28454  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28455 .x-view-drag-insert-above {
28456         border-top:1px dotted #3366cc;
28457 }
28458 .x-view-drag-insert-below {
28459         border-bottom:1px dotted #3366cc;
28460 }
28461 </code></pre>
28462  * 
28463  */
28464  
28465 Roo.DDView = function(container, tpl, config) {
28466     Roo.DDView.superclass.constructor.apply(this, arguments);
28467     this.getEl().setStyle("outline", "0px none");
28468     this.getEl().unselectable();
28469     if (this.dragGroup) {
28470         this.setDraggable(this.dragGroup.split(","));
28471     }
28472     if (this.dropGroup) {
28473         this.setDroppable(this.dropGroup.split(","));
28474     }
28475     if (this.deletable) {
28476         this.setDeletable();
28477     }
28478     this.isDirtyFlag = false;
28479         this.addEvents({
28480                 "drop" : true
28481         });
28482 };
28483
28484 Roo.extend(Roo.DDView, Roo.View, {
28485 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28486 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28487 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28488 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28489
28490         isFormField: true,
28491
28492         reset: Roo.emptyFn,
28493         
28494         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28495
28496         validate: function() {
28497                 return true;
28498         },
28499         
28500         destroy: function() {
28501                 this.purgeListeners();
28502                 this.getEl.removeAllListeners();
28503                 this.getEl().remove();
28504                 if (this.dragZone) {
28505                         if (this.dragZone.destroy) {
28506                                 this.dragZone.destroy();
28507                         }
28508                 }
28509                 if (this.dropZone) {
28510                         if (this.dropZone.destroy) {
28511                                 this.dropZone.destroy();
28512                         }
28513                 }
28514         },
28515
28516 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28517         getName: function() {
28518                 return this.name;
28519         },
28520
28521 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28522         setValue: function(v) {
28523                 if (!this.store) {
28524                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28525                 }
28526                 var data = {};
28527                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28528                 this.store.proxy = new Roo.data.MemoryProxy(data);
28529                 this.store.load();
28530         },
28531
28532 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28533         getValue: function() {
28534                 var result = '(';
28535                 this.store.each(function(rec) {
28536                         result += rec.id + ',';
28537                 });
28538                 return result.substr(0, result.length - 1) + ')';
28539         },
28540         
28541         getIds: function() {
28542                 var i = 0, result = new Array(this.store.getCount());
28543                 this.store.each(function(rec) {
28544                         result[i++] = rec.id;
28545                 });
28546                 return result;
28547         },
28548         
28549         isDirty: function() {
28550                 return this.isDirtyFlag;
28551         },
28552
28553 /**
28554  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28555  *      whole Element becomes the target, and this causes the drop gesture to append.
28556  */
28557     getTargetFromEvent : function(e) {
28558                 var target = e.getTarget();
28559                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28560                 target = target.parentNode;
28561                 }
28562                 if (!target) {
28563                         target = this.el.dom.lastChild || this.el.dom;
28564                 }
28565                 return target;
28566     },
28567
28568 /**
28569  *      Create the drag data which consists of an object which has the property "ddel" as
28570  *      the drag proxy element. 
28571  */
28572     getDragData : function(e) {
28573         var target = this.findItemFromChild(e.getTarget());
28574                 if(target) {
28575                         this.handleSelection(e);
28576                         var selNodes = this.getSelectedNodes();
28577             var dragData = {
28578                 source: this,
28579                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28580                 nodes: selNodes,
28581                 records: []
28582                         };
28583                         var selectedIndices = this.getSelectedIndexes();
28584                         for (var i = 0; i < selectedIndices.length; i++) {
28585                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28586                         }
28587                         if (selNodes.length == 1) {
28588                                 dragData.ddel = target.cloneNode(true); // the div element
28589                         } else {
28590                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28591                                 div.className = 'multi-proxy';
28592                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28593                                         div.appendChild(selNodes[i].cloneNode(true));
28594                                 }
28595                                 dragData.ddel = div;
28596                         }
28597             //console.log(dragData)
28598             //console.log(dragData.ddel.innerHTML)
28599                         return dragData;
28600                 }
28601         //console.log('nodragData')
28602                 return false;
28603     },
28604     
28605 /**     Specify to which ddGroup items in this DDView may be dragged. */
28606     setDraggable: function(ddGroup) {
28607         if (ddGroup instanceof Array) {
28608                 Roo.each(ddGroup, this.setDraggable, this);
28609                 return;
28610         }
28611         if (this.dragZone) {
28612                 this.dragZone.addToGroup(ddGroup);
28613         } else {
28614                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28615                                 containerScroll: true,
28616                                 ddGroup: ddGroup 
28617
28618                         });
28619 //                      Draggability implies selection. DragZone's mousedown selects the element.
28620                         if (!this.multiSelect) { this.singleSelect = true; }
28621
28622 //                      Wire the DragZone's handlers up to methods in *this*
28623                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28624                 }
28625     },
28626
28627 /**     Specify from which ddGroup this DDView accepts drops. */
28628     setDroppable: function(ddGroup) {
28629         if (ddGroup instanceof Array) {
28630                 Roo.each(ddGroup, this.setDroppable, this);
28631                 return;
28632         }
28633         if (this.dropZone) {
28634                 this.dropZone.addToGroup(ddGroup);
28635         } else {
28636                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28637                                 containerScroll: true,
28638                                 ddGroup: ddGroup
28639                         });
28640
28641 //                      Wire the DropZone's handlers up to methods in *this*
28642                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28643                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28644                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28645                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28646                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28647                 }
28648     },
28649
28650 /**     Decide whether to drop above or below a View node. */
28651     getDropPoint : function(e, n, dd){
28652         if (n == this.el.dom) { return "above"; }
28653                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28654                 var c = t + (b - t) / 2;
28655                 var y = Roo.lib.Event.getPageY(e);
28656                 if(y <= c) {
28657                         return "above";
28658                 }else{
28659                         return "below";
28660                 }
28661     },
28662
28663     onNodeEnter : function(n, dd, e, data){
28664                 return false;
28665     },
28666     
28667     onNodeOver : function(n, dd, e, data){
28668                 var pt = this.getDropPoint(e, n, dd);
28669                 // set the insert point style on the target node
28670                 var dragElClass = this.dropNotAllowed;
28671                 if (pt) {
28672                         var targetElClass;
28673                         if (pt == "above"){
28674                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28675                                 targetElClass = "x-view-drag-insert-above";
28676                         } else {
28677                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28678                                 targetElClass = "x-view-drag-insert-below";
28679                         }
28680                         if (this.lastInsertClass != targetElClass){
28681                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28682                                 this.lastInsertClass = targetElClass;
28683                         }
28684                 }
28685                 return dragElClass;
28686         },
28687
28688     onNodeOut : function(n, dd, e, data){
28689                 this.removeDropIndicators(n);
28690     },
28691
28692     onNodeDrop : function(n, dd, e, data){
28693         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28694                 return false;
28695         }
28696         var pt = this.getDropPoint(e, n, dd);
28697                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28698                 if (pt == "below") { insertAt++; }
28699                 for (var i = 0; i < data.records.length; i++) {
28700                         var r = data.records[i];
28701                         var dup = this.store.getById(r.id);
28702                         if (dup && (dd != this.dragZone)) {
28703                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28704                         } else {
28705                                 if (data.copy) {
28706                                         this.store.insert(insertAt++, r.copy());
28707                                 } else {
28708                                         data.source.isDirtyFlag = true;
28709                                         r.store.remove(r);
28710                                         this.store.insert(insertAt++, r);
28711                                 }
28712                                 this.isDirtyFlag = true;
28713                         }
28714                 }
28715                 this.dragZone.cachedTarget = null;
28716                 return true;
28717     },
28718
28719     removeDropIndicators : function(n){
28720                 if(n){
28721                         Roo.fly(n).removeClass([
28722                                 "x-view-drag-insert-above",
28723                                 "x-view-drag-insert-below"]);
28724                         this.lastInsertClass = "_noclass";
28725                 }
28726     },
28727
28728 /**
28729  *      Utility method. Add a delete option to the DDView's context menu.
28730  *      @param {String} imageUrl The URL of the "delete" icon image.
28731  */
28732         setDeletable: function(imageUrl) {
28733                 if (!this.singleSelect && !this.multiSelect) {
28734                         this.singleSelect = true;
28735                 }
28736                 var c = this.getContextMenu();
28737                 this.contextMenu.on("itemclick", function(item) {
28738                         switch (item.id) {
28739                                 case "delete":
28740                                         this.remove(this.getSelectedIndexes());
28741                                         break;
28742                         }
28743                 }, this);
28744                 this.contextMenu.add({
28745                         icon: imageUrl,
28746                         id: "delete",
28747                         text: 'Delete'
28748                 });
28749         },
28750         
28751 /**     Return the context menu for this DDView. */
28752         getContextMenu: function() {
28753                 if (!this.contextMenu) {
28754 //                      Create the View's context menu
28755                         this.contextMenu = new Roo.menu.Menu({
28756                                 id: this.id + "-contextmenu"
28757                         });
28758                         this.el.on("contextmenu", this.showContextMenu, this);
28759                 }
28760                 return this.contextMenu;
28761         },
28762         
28763         disableContextMenu: function() {
28764                 if (this.contextMenu) {
28765                         this.el.un("contextmenu", this.showContextMenu, this);
28766                 }
28767         },
28768
28769         showContextMenu: function(e, item) {
28770         item = this.findItemFromChild(e.getTarget());
28771                 if (item) {
28772                         e.stopEvent();
28773                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28774                         this.contextMenu.showAt(e.getXY());
28775             }
28776     },
28777
28778 /**
28779  *      Remove {@link Roo.data.Record}s at the specified indices.
28780  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28781  */
28782     remove: function(selectedIndices) {
28783                 selectedIndices = [].concat(selectedIndices);
28784                 for (var i = 0; i < selectedIndices.length; i++) {
28785                         var rec = this.store.getAt(selectedIndices[i]);
28786                         this.store.remove(rec);
28787                 }
28788     },
28789
28790 /**
28791  *      Double click fires the event, but also, if this is draggable, and there is only one other
28792  *      related DropZone, it transfers the selected node.
28793  */
28794     onDblClick : function(e){
28795         var item = this.findItemFromChild(e.getTarget());
28796         if(item){
28797             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28798                 return false;
28799             }
28800             if (this.dragGroup) {
28801                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28802                     while (targets.indexOf(this.dropZone) > -1) {
28803                             targets.remove(this.dropZone);
28804                                 }
28805                     if (targets.length == 1) {
28806                                         this.dragZone.cachedTarget = null;
28807                         var el = Roo.get(targets[0].getEl());
28808                         var box = el.getBox(true);
28809                         targets[0].onNodeDrop(el.dom, {
28810                                 target: el.dom,
28811                                 xy: [box.x, box.y + box.height - 1]
28812                         }, null, this.getDragData(e));
28813                     }
28814                 }
28815         }
28816     },
28817     
28818     handleSelection: function(e) {
28819                 this.dragZone.cachedTarget = null;
28820         var item = this.findItemFromChild(e.getTarget());
28821         if (!item) {
28822                 this.clearSelections(true);
28823                 return;
28824         }
28825                 if (item && (this.multiSelect || this.singleSelect)){
28826                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28827                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28828                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28829                                 this.unselect(item);
28830                         } else {
28831                                 this.select(item, this.multiSelect && e.ctrlKey);
28832                                 this.lastSelection = item;
28833                         }
28834                 }
28835     },
28836
28837     onItemClick : function(item, index, e){
28838                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28839                         return false;
28840                 }
28841                 return true;
28842     },
28843
28844     unselect : function(nodeInfo, suppressEvent){
28845                 var node = this.getNode(nodeInfo);
28846                 if(node && this.isSelected(node)){
28847                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28848                                 Roo.fly(node).removeClass(this.selectedClass);
28849                                 this.selections.remove(node);
28850                                 if(!suppressEvent){
28851                                         this.fireEvent("selectionchange", this, this.selections);
28852                                 }
28853                         }
28854                 }
28855     }
28856 });
28857 /*
28858  * Based on:
28859  * Ext JS Library 1.1.1
28860  * Copyright(c) 2006-2007, Ext JS, LLC.
28861  *
28862  * Originally Released Under LGPL - original licence link has changed is not relivant.
28863  *
28864  * Fork - LGPL
28865  * <script type="text/javascript">
28866  */
28867  
28868 /**
28869  * @class Roo.LayoutManager
28870  * @extends Roo.util.Observable
28871  * Base class for layout managers.
28872  */
28873 Roo.LayoutManager = function(container, config){
28874     Roo.LayoutManager.superclass.constructor.call(this);
28875     this.el = Roo.get(container);
28876     // ie scrollbar fix
28877     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28878         document.body.scroll = "no";
28879     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28880         this.el.position('relative');
28881     }
28882     this.id = this.el.id;
28883     this.el.addClass("x-layout-container");
28884     /** false to disable window resize monitoring @type Boolean */
28885     this.monitorWindowResize = true;
28886     this.regions = {};
28887     this.addEvents({
28888         /**
28889          * @event layout
28890          * Fires when a layout is performed. 
28891          * @param {Roo.LayoutManager} this
28892          */
28893         "layout" : true,
28894         /**
28895          * @event regionresized
28896          * Fires when the user resizes a region. 
28897          * @param {Roo.LayoutRegion} region The resized region
28898          * @param {Number} newSize The new size (width for east/west, height for north/south)
28899          */
28900         "regionresized" : true,
28901         /**
28902          * @event regioncollapsed
28903          * Fires when a region is collapsed. 
28904          * @param {Roo.LayoutRegion} region The collapsed region
28905          */
28906         "regioncollapsed" : true,
28907         /**
28908          * @event regionexpanded
28909          * Fires when a region is expanded.  
28910          * @param {Roo.LayoutRegion} region The expanded region
28911          */
28912         "regionexpanded" : true
28913     });
28914     this.updating = false;
28915     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28916 };
28917
28918 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28919     /**
28920      * Returns true if this layout is currently being updated
28921      * @return {Boolean}
28922      */
28923     isUpdating : function(){
28924         return this.updating; 
28925     },
28926     
28927     /**
28928      * Suspend the LayoutManager from doing auto-layouts while
28929      * making multiple add or remove calls
28930      */
28931     beginUpdate : function(){
28932         this.updating = true;    
28933     },
28934     
28935     /**
28936      * Restore auto-layouts and optionally disable the manager from performing a layout
28937      * @param {Boolean} noLayout true to disable a layout update 
28938      */
28939     endUpdate : function(noLayout){
28940         this.updating = false;
28941         if(!noLayout){
28942             this.layout();
28943         }    
28944     },
28945     
28946     layout: function(){
28947         
28948     },
28949     
28950     onRegionResized : function(region, newSize){
28951         this.fireEvent("regionresized", region, newSize);
28952         this.layout();
28953     },
28954     
28955     onRegionCollapsed : function(region){
28956         this.fireEvent("regioncollapsed", region);
28957     },
28958     
28959     onRegionExpanded : function(region){
28960         this.fireEvent("regionexpanded", region);
28961     },
28962         
28963     /**
28964      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28965      * performs box-model adjustments.
28966      * @return {Object} The size as an object {width: (the width), height: (the height)}
28967      */
28968     getViewSize : function(){
28969         var size;
28970         if(this.el.dom != document.body){
28971             size = this.el.getSize();
28972         }else{
28973             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28974         }
28975         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28976         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28977         return size;
28978     },
28979     
28980     /**
28981      * Returns the Element this layout is bound to.
28982      * @return {Roo.Element}
28983      */
28984     getEl : function(){
28985         return this.el;
28986     },
28987     
28988     /**
28989      * Returns the specified region.
28990      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28991      * @return {Roo.LayoutRegion}
28992      */
28993     getRegion : function(target){
28994         return this.regions[target.toLowerCase()];
28995     },
28996     
28997     onWindowResize : function(){
28998         if(this.monitorWindowResize){
28999             this.layout();
29000         }
29001     }
29002 });/*
29003  * Based on:
29004  * Ext JS Library 1.1.1
29005  * Copyright(c) 2006-2007, Ext JS, LLC.
29006  *
29007  * Originally Released Under LGPL - original licence link has changed is not relivant.
29008  *
29009  * Fork - LGPL
29010  * <script type="text/javascript">
29011  */
29012 /**
29013  * @class Roo.BorderLayout
29014  * @extends Roo.LayoutManager
29015  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29016  * please see: <br><br>
29017  * <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>
29018  * <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>
29019  * Example:
29020  <pre><code>
29021  var layout = new Roo.BorderLayout(document.body, {
29022     north: {
29023         initialSize: 25,
29024         titlebar: false
29025     },
29026     west: {
29027         split:true,
29028         initialSize: 200,
29029         minSize: 175,
29030         maxSize: 400,
29031         titlebar: true,
29032         collapsible: true
29033     },
29034     east: {
29035         split:true,
29036         initialSize: 202,
29037         minSize: 175,
29038         maxSize: 400,
29039         titlebar: true,
29040         collapsible: true
29041     },
29042     south: {
29043         split:true,
29044         initialSize: 100,
29045         minSize: 100,
29046         maxSize: 200,
29047         titlebar: true,
29048         collapsible: true
29049     },
29050     center: {
29051         titlebar: true,
29052         autoScroll:true,
29053         resizeTabs: true,
29054         minTabWidth: 50,
29055         preferredTabWidth: 150
29056     }
29057 });
29058
29059 // shorthand
29060 var CP = Roo.ContentPanel;
29061
29062 layout.beginUpdate();
29063 layout.add("north", new CP("north", "North"));
29064 layout.add("south", new CP("south", {title: "South", closable: true}));
29065 layout.add("west", new CP("west", {title: "West"}));
29066 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29067 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29068 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29069 layout.getRegion("center").showPanel("center1");
29070 layout.endUpdate();
29071 </code></pre>
29072
29073 <b>The container the layout is rendered into can be either the body element or any other element.
29074 If it is not the body element, the container needs to either be an absolute positioned element,
29075 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29076 the container size if it is not the body element.</b>
29077
29078 * @constructor
29079 * Create a new BorderLayout
29080 * @param {String/HTMLElement/Element} container The container this layout is bound to
29081 * @param {Object} config Configuration options
29082  */
29083 Roo.BorderLayout = function(container, config){
29084     config = config || {};
29085     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29086     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29087     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29088         var target = this.factory.validRegions[i];
29089         if(config[target]){
29090             this.addRegion(target, config[target]);
29091         }
29092     }
29093 };
29094
29095 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29096     /**
29097      * Creates and adds a new region if it doesn't already exist.
29098      * @param {String} target The target region key (north, south, east, west or center).
29099      * @param {Object} config The regions config object
29100      * @return {BorderLayoutRegion} The new region
29101      */
29102     addRegion : function(target, config){
29103         if(!this.regions[target]){
29104             var r = this.factory.create(target, this, config);
29105             this.bindRegion(target, r);
29106         }
29107         return this.regions[target];
29108     },
29109
29110     // private (kinda)
29111     bindRegion : function(name, r){
29112         this.regions[name] = r;
29113         r.on("visibilitychange", this.layout, this);
29114         r.on("paneladded", this.layout, this);
29115         r.on("panelremoved", this.layout, this);
29116         r.on("invalidated", this.layout, this);
29117         r.on("resized", this.onRegionResized, this);
29118         r.on("collapsed", this.onRegionCollapsed, this);
29119         r.on("expanded", this.onRegionExpanded, this);
29120     },
29121
29122     /**
29123      * Performs a layout update.
29124      */
29125     layout : function(){
29126         if(this.updating) {
29127             return;
29128         }
29129         var size = this.getViewSize();
29130         var w = size.width;
29131         var h = size.height;
29132         var centerW = w;
29133         var centerH = h;
29134         var centerY = 0;
29135         var centerX = 0;
29136         //var x = 0, y = 0;
29137
29138         var rs = this.regions;
29139         var north = rs["north"];
29140         var south = rs["south"]; 
29141         var west = rs["west"];
29142         var east = rs["east"];
29143         var center = rs["center"];
29144         //if(this.hideOnLayout){ // not supported anymore
29145             //c.el.setStyle("display", "none");
29146         //}
29147         if(north && north.isVisible()){
29148             var b = north.getBox();
29149             var m = north.getMargins();
29150             b.width = w - (m.left+m.right);
29151             b.x = m.left;
29152             b.y = m.top;
29153             centerY = b.height + b.y + m.bottom;
29154             centerH -= centerY;
29155             north.updateBox(this.safeBox(b));
29156         }
29157         if(south && south.isVisible()){
29158             var b = south.getBox();
29159             var m = south.getMargins();
29160             b.width = w - (m.left+m.right);
29161             b.x = m.left;
29162             var totalHeight = (b.height + m.top + m.bottom);
29163             b.y = h - totalHeight + m.top;
29164             centerH -= totalHeight;
29165             south.updateBox(this.safeBox(b));
29166         }
29167         if(west && west.isVisible()){
29168             var b = west.getBox();
29169             var m = west.getMargins();
29170             b.height = centerH - (m.top+m.bottom);
29171             b.x = m.left;
29172             b.y = centerY + m.top;
29173             var totalWidth = (b.width + m.left + m.right);
29174             centerX += totalWidth;
29175             centerW -= totalWidth;
29176             west.updateBox(this.safeBox(b));
29177         }
29178         if(east && east.isVisible()){
29179             var b = east.getBox();
29180             var m = east.getMargins();
29181             b.height = centerH - (m.top+m.bottom);
29182             var totalWidth = (b.width + m.left + m.right);
29183             b.x = w - totalWidth + m.left;
29184             b.y = centerY + m.top;
29185             centerW -= totalWidth;
29186             east.updateBox(this.safeBox(b));
29187         }
29188         if(center){
29189             var m = center.getMargins();
29190             var centerBox = {
29191                 x: centerX + m.left,
29192                 y: centerY + m.top,
29193                 width: centerW - (m.left+m.right),
29194                 height: centerH - (m.top+m.bottom)
29195             };
29196             //if(this.hideOnLayout){
29197                 //center.el.setStyle("display", "block");
29198             //}
29199             center.updateBox(this.safeBox(centerBox));
29200         }
29201         this.el.repaint();
29202         this.fireEvent("layout", this);
29203     },
29204
29205     // private
29206     safeBox : function(box){
29207         box.width = Math.max(0, box.width);
29208         box.height = Math.max(0, box.height);
29209         return box;
29210     },
29211
29212     /**
29213      * Adds a ContentPanel (or subclass) to this layout.
29214      * @param {String} target The target region key (north, south, east, west or center).
29215      * @param {Roo.ContentPanel} panel The panel to add
29216      * @return {Roo.ContentPanel} The added panel
29217      */
29218     add : function(target, panel){
29219          
29220         target = target.toLowerCase();
29221         return this.regions[target].add(panel);
29222     },
29223
29224     /**
29225      * Remove a ContentPanel (or subclass) to this layout.
29226      * @param {String} target The target region key (north, south, east, west or center).
29227      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29228      * @return {Roo.ContentPanel} The removed panel
29229      */
29230     remove : function(target, panel){
29231         target = target.toLowerCase();
29232         return this.regions[target].remove(panel);
29233     },
29234
29235     /**
29236      * Searches all regions for a panel with the specified id
29237      * @param {String} panelId
29238      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29239      */
29240     findPanel : function(panelId){
29241         var rs = this.regions;
29242         for(var target in rs){
29243             if(typeof rs[target] != "function"){
29244                 var p = rs[target].getPanel(panelId);
29245                 if(p){
29246                     return p;
29247                 }
29248             }
29249         }
29250         return null;
29251     },
29252
29253     /**
29254      * Searches all regions for a panel with the specified id and activates (shows) it.
29255      * @param {String/ContentPanel} panelId The panels id or the panel itself
29256      * @return {Roo.ContentPanel} The shown panel or null
29257      */
29258     showPanel : function(panelId) {
29259       var rs = this.regions;
29260       for(var target in rs){
29261          var r = rs[target];
29262          if(typeof r != "function"){
29263             if(r.hasPanel(panelId)){
29264                return r.showPanel(panelId);
29265             }
29266          }
29267       }
29268       return null;
29269    },
29270
29271    /**
29272      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29273      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29274      */
29275     restoreState : function(provider){
29276         if(!provider){
29277             provider = Roo.state.Manager;
29278         }
29279         var sm = new Roo.LayoutStateManager();
29280         sm.init(this, provider);
29281     },
29282
29283     /**
29284      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29285      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29286      * a valid ContentPanel config object.  Example:
29287      * <pre><code>
29288 // Create the main layout
29289 var layout = new Roo.BorderLayout('main-ct', {
29290     west: {
29291         split:true,
29292         minSize: 175,
29293         titlebar: true
29294     },
29295     center: {
29296         title:'Components'
29297     }
29298 }, 'main-ct');
29299
29300 // Create and add multiple ContentPanels at once via configs
29301 layout.batchAdd({
29302    west: {
29303        id: 'source-files',
29304        autoCreate:true,
29305        title:'Ext Source Files',
29306        autoScroll:true,
29307        fitToFrame:true
29308    },
29309    center : {
29310        el: cview,
29311        autoScroll:true,
29312        fitToFrame:true,
29313        toolbar: tb,
29314        resizeEl:'cbody'
29315    }
29316 });
29317 </code></pre>
29318      * @param {Object} regions An object containing ContentPanel configs by region name
29319      */
29320     batchAdd : function(regions){
29321         this.beginUpdate();
29322         for(var rname in regions){
29323             var lr = this.regions[rname];
29324             if(lr){
29325                 this.addTypedPanels(lr, regions[rname]);
29326             }
29327         }
29328         this.endUpdate();
29329     },
29330
29331     // private
29332     addTypedPanels : function(lr, ps){
29333         if(typeof ps == 'string'){
29334             lr.add(new Roo.ContentPanel(ps));
29335         }
29336         else if(ps instanceof Array){
29337             for(var i =0, len = ps.length; i < len; i++){
29338                 this.addTypedPanels(lr, ps[i]);
29339             }
29340         }
29341         else if(!ps.events){ // raw config?
29342             var el = ps.el;
29343             delete ps.el; // prevent conflict
29344             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29345         }
29346         else {  // panel object assumed!
29347             lr.add(ps);
29348         }
29349     },
29350     /**
29351      * Adds a xtype elements to the layout.
29352      * <pre><code>
29353
29354 layout.addxtype({
29355        xtype : 'ContentPanel',
29356        region: 'west',
29357        items: [ .... ]
29358    }
29359 );
29360
29361 layout.addxtype({
29362         xtype : 'NestedLayoutPanel',
29363         region: 'west',
29364         layout: {
29365            center: { },
29366            west: { }   
29367         },
29368         items : [ ... list of content panels or nested layout panels.. ]
29369    }
29370 );
29371 </code></pre>
29372      * @param {Object} cfg Xtype definition of item to add.
29373      */
29374     addxtype : function(cfg)
29375     {
29376         // basically accepts a pannel...
29377         // can accept a layout region..!?!?
29378         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29379         
29380         if (!cfg.xtype.match(/Panel$/)) {
29381             return false;
29382         }
29383         var ret = false;
29384         
29385         if (typeof(cfg.region) == 'undefined') {
29386             Roo.log("Failed to add Panel, region was not set");
29387             Roo.log(cfg);
29388             return false;
29389         }
29390         var region = cfg.region;
29391         delete cfg.region;
29392         
29393           
29394         var xitems = [];
29395         if (cfg.items) {
29396             xitems = cfg.items;
29397             delete cfg.items;
29398         }
29399         var nb = false;
29400         
29401         switch(cfg.xtype) 
29402         {
29403             case 'ContentPanel':  // ContentPanel (el, cfg)
29404             case 'ScrollPanel':  // ContentPanel (el, cfg)
29405             case 'ViewPanel': 
29406                 if(cfg.autoCreate) {
29407                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29408                 } else {
29409                     var el = this.el.createChild();
29410                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29411                 }
29412                 
29413                 this.add(region, ret);
29414                 break;
29415             
29416             
29417             case 'TreePanel': // our new panel!
29418                 cfg.el = this.el.createChild();
29419                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29420                 this.add(region, ret);
29421                 break;
29422             
29423             case 'NestedLayoutPanel': 
29424                 // create a new Layout (which is  a Border Layout...
29425                 var el = this.el.createChild();
29426                 var clayout = cfg.layout;
29427                 delete cfg.layout;
29428                 clayout.items   = clayout.items  || [];
29429                 // replace this exitems with the clayout ones..
29430                 xitems = clayout.items;
29431                  
29432                 
29433                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29434                     cfg.background = false;
29435                 }
29436                 var layout = new Roo.BorderLayout(el, clayout);
29437                 
29438                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29439                 //console.log('adding nested layout panel '  + cfg.toSource());
29440                 this.add(region, ret);
29441                 nb = {}; /// find first...
29442                 break;
29443                 
29444             case 'GridPanel': 
29445             
29446                 // needs grid and region
29447                 
29448                 //var el = this.getRegion(region).el.createChild();
29449                 var el = this.el.createChild();
29450                 // create the grid first...
29451                 
29452                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29453                 delete cfg.grid;
29454                 if (region == 'center' && this.active ) {
29455                     cfg.background = false;
29456                 }
29457                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29458                 
29459                 this.add(region, ret);
29460                 if (cfg.background) {
29461                     ret.on('activate', function(gp) {
29462                         if (!gp.grid.rendered) {
29463                             gp.grid.render();
29464                         }
29465                     });
29466                 } else {
29467                     grid.render();
29468                 }
29469                 break;
29470            
29471            
29472            
29473                 
29474                 
29475                 
29476             default:
29477                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29478                     
29479                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29480                     this.add(region, ret);
29481                 } else {
29482                 
29483                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29484                     return null;
29485                 }
29486                 
29487              // GridPanel (grid, cfg)
29488             
29489         }
29490         this.beginUpdate();
29491         // add children..
29492         var region = '';
29493         var abn = {};
29494         Roo.each(xitems, function(i)  {
29495             region = nb && i.region ? i.region : false;
29496             
29497             var add = ret.addxtype(i);
29498            
29499             if (region) {
29500                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29501                 if (!i.background) {
29502                     abn[region] = nb[region] ;
29503                 }
29504             }
29505             
29506         });
29507         this.endUpdate();
29508
29509         // make the last non-background panel active..
29510         //if (nb) { Roo.log(abn); }
29511         if (nb) {
29512             
29513             for(var r in abn) {
29514                 region = this.getRegion(r);
29515                 if (region) {
29516                     // tried using nb[r], but it does not work..
29517                      
29518                     region.showPanel(abn[r]);
29519                    
29520                 }
29521             }
29522         }
29523         return ret;
29524         
29525     }
29526 });
29527
29528 /**
29529  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29530  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29531  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29532  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29533  * <pre><code>
29534 // shorthand
29535 var CP = Roo.ContentPanel;
29536
29537 var layout = Roo.BorderLayout.create({
29538     north: {
29539         initialSize: 25,
29540         titlebar: false,
29541         panels: [new CP("north", "North")]
29542     },
29543     west: {
29544         split:true,
29545         initialSize: 200,
29546         minSize: 175,
29547         maxSize: 400,
29548         titlebar: true,
29549         collapsible: true,
29550         panels: [new CP("west", {title: "West"})]
29551     },
29552     east: {
29553         split:true,
29554         initialSize: 202,
29555         minSize: 175,
29556         maxSize: 400,
29557         titlebar: true,
29558         collapsible: true,
29559         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29560     },
29561     south: {
29562         split:true,
29563         initialSize: 100,
29564         minSize: 100,
29565         maxSize: 200,
29566         titlebar: true,
29567         collapsible: true,
29568         panels: [new CP("south", {title: "South", closable: true})]
29569     },
29570     center: {
29571         titlebar: true,
29572         autoScroll:true,
29573         resizeTabs: true,
29574         minTabWidth: 50,
29575         preferredTabWidth: 150,
29576         panels: [
29577             new CP("center1", {title: "Close Me", closable: true}),
29578             new CP("center2", {title: "Center Panel", closable: false})
29579         ]
29580     }
29581 }, document.body);
29582
29583 layout.getRegion("center").showPanel("center1");
29584 </code></pre>
29585  * @param config
29586  * @param targetEl
29587  */
29588 Roo.BorderLayout.create = function(config, targetEl){
29589     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29590     layout.beginUpdate();
29591     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29592     for(var j = 0, jlen = regions.length; j < jlen; j++){
29593         var lr = regions[j];
29594         if(layout.regions[lr] && config[lr].panels){
29595             var r = layout.regions[lr];
29596             var ps = config[lr].panels;
29597             layout.addTypedPanels(r, ps);
29598         }
29599     }
29600     layout.endUpdate();
29601     return layout;
29602 };
29603
29604 // private
29605 Roo.BorderLayout.RegionFactory = {
29606     // private
29607     validRegions : ["north","south","east","west","center"],
29608
29609     // private
29610     create : function(target, mgr, config){
29611         target = target.toLowerCase();
29612         if(config.lightweight || config.basic){
29613             return new Roo.BasicLayoutRegion(mgr, config, target);
29614         }
29615         switch(target){
29616             case "north":
29617                 return new Roo.NorthLayoutRegion(mgr, config);
29618             case "south":
29619                 return new Roo.SouthLayoutRegion(mgr, config);
29620             case "east":
29621                 return new Roo.EastLayoutRegion(mgr, config);
29622             case "west":
29623                 return new Roo.WestLayoutRegion(mgr, config);
29624             case "center":
29625                 return new Roo.CenterLayoutRegion(mgr, config);
29626         }
29627         throw 'Layout region "'+target+'" not supported.';
29628     }
29629 };/*
29630  * Based on:
29631  * Ext JS Library 1.1.1
29632  * Copyright(c) 2006-2007, Ext JS, LLC.
29633  *
29634  * Originally Released Under LGPL - original licence link has changed is not relivant.
29635  *
29636  * Fork - LGPL
29637  * <script type="text/javascript">
29638  */
29639  
29640 /**
29641  * @class Roo.BasicLayoutRegion
29642  * @extends Roo.util.Observable
29643  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29644  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29645  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29646  */
29647 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29648     this.mgr = mgr;
29649     this.position  = pos;
29650     this.events = {
29651         /**
29652          * @scope Roo.BasicLayoutRegion
29653          */
29654         
29655         /**
29656          * @event beforeremove
29657          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29658          * @param {Roo.LayoutRegion} this
29659          * @param {Roo.ContentPanel} panel The panel
29660          * @param {Object} e The cancel event object
29661          */
29662         "beforeremove" : true,
29663         /**
29664          * @event invalidated
29665          * Fires when the layout for this region is changed.
29666          * @param {Roo.LayoutRegion} this
29667          */
29668         "invalidated" : true,
29669         /**
29670          * @event visibilitychange
29671          * Fires when this region is shown or hidden 
29672          * @param {Roo.LayoutRegion} this
29673          * @param {Boolean} visibility true or false
29674          */
29675         "visibilitychange" : true,
29676         /**
29677          * @event paneladded
29678          * Fires when a panel is added. 
29679          * @param {Roo.LayoutRegion} this
29680          * @param {Roo.ContentPanel} panel The panel
29681          */
29682         "paneladded" : true,
29683         /**
29684          * @event panelremoved
29685          * Fires when a panel is removed. 
29686          * @param {Roo.LayoutRegion} this
29687          * @param {Roo.ContentPanel} panel The panel
29688          */
29689         "panelremoved" : true,
29690         /**
29691          * @event beforecollapse
29692          * Fires when this region before collapse.
29693          * @param {Roo.LayoutRegion} this
29694          */
29695         "beforecollapse" : true,
29696         /**
29697          * @event collapsed
29698          * Fires when this region is collapsed.
29699          * @param {Roo.LayoutRegion} this
29700          */
29701         "collapsed" : true,
29702         /**
29703          * @event expanded
29704          * Fires when this region is expanded.
29705          * @param {Roo.LayoutRegion} this
29706          */
29707         "expanded" : true,
29708         /**
29709          * @event slideshow
29710          * Fires when this region is slid into view.
29711          * @param {Roo.LayoutRegion} this
29712          */
29713         "slideshow" : true,
29714         /**
29715          * @event slidehide
29716          * Fires when this region slides out of view. 
29717          * @param {Roo.LayoutRegion} this
29718          */
29719         "slidehide" : true,
29720         /**
29721          * @event panelactivated
29722          * Fires when a panel is activated. 
29723          * @param {Roo.LayoutRegion} this
29724          * @param {Roo.ContentPanel} panel The activated panel
29725          */
29726         "panelactivated" : true,
29727         /**
29728          * @event resized
29729          * Fires when the user resizes this region. 
29730          * @param {Roo.LayoutRegion} this
29731          * @param {Number} newSize The new size (width for east/west, height for north/south)
29732          */
29733         "resized" : true
29734     };
29735     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29736     this.panels = new Roo.util.MixedCollection();
29737     this.panels.getKey = this.getPanelId.createDelegate(this);
29738     this.box = null;
29739     this.activePanel = null;
29740     // ensure listeners are added...
29741     
29742     if (config.listeners || config.events) {
29743         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29744             listeners : config.listeners || {},
29745             events : config.events || {}
29746         });
29747     }
29748     
29749     if(skipConfig !== true){
29750         this.applyConfig(config);
29751     }
29752 };
29753
29754 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29755     getPanelId : function(p){
29756         return p.getId();
29757     },
29758     
29759     applyConfig : function(config){
29760         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29761         this.config = config;
29762         
29763     },
29764     
29765     /**
29766      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29767      * the width, for horizontal (north, south) the height.
29768      * @param {Number} newSize The new width or height
29769      */
29770     resizeTo : function(newSize){
29771         var el = this.el ? this.el :
29772                  (this.activePanel ? this.activePanel.getEl() : null);
29773         if(el){
29774             switch(this.position){
29775                 case "east":
29776                 case "west":
29777                     el.setWidth(newSize);
29778                     this.fireEvent("resized", this, newSize);
29779                 break;
29780                 case "north":
29781                 case "south":
29782                     el.setHeight(newSize);
29783                     this.fireEvent("resized", this, newSize);
29784                 break;                
29785             }
29786         }
29787     },
29788     
29789     getBox : function(){
29790         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29791     },
29792     
29793     getMargins : function(){
29794         return this.margins;
29795     },
29796     
29797     updateBox : function(box){
29798         this.box = box;
29799         var el = this.activePanel.getEl();
29800         el.dom.style.left = box.x + "px";
29801         el.dom.style.top = box.y + "px";
29802         this.activePanel.setSize(box.width, box.height);
29803     },
29804     
29805     /**
29806      * Returns the container element for this region.
29807      * @return {Roo.Element}
29808      */
29809     getEl : function(){
29810         return this.activePanel;
29811     },
29812     
29813     /**
29814      * Returns true if this region is currently visible.
29815      * @return {Boolean}
29816      */
29817     isVisible : function(){
29818         return this.activePanel ? true : false;
29819     },
29820     
29821     setActivePanel : function(panel){
29822         panel = this.getPanel(panel);
29823         if(this.activePanel && this.activePanel != panel){
29824             this.activePanel.setActiveState(false);
29825             this.activePanel.getEl().setLeftTop(-10000,-10000);
29826         }
29827         this.activePanel = panel;
29828         panel.setActiveState(true);
29829         if(this.box){
29830             panel.setSize(this.box.width, this.box.height);
29831         }
29832         this.fireEvent("panelactivated", this, panel);
29833         this.fireEvent("invalidated");
29834     },
29835     
29836     /**
29837      * Show the specified panel.
29838      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29839      * @return {Roo.ContentPanel} The shown panel or null
29840      */
29841     showPanel : function(panel){
29842         if(panel = this.getPanel(panel)){
29843             this.setActivePanel(panel);
29844         }
29845         return panel;
29846     },
29847     
29848     /**
29849      * Get the active panel for this region.
29850      * @return {Roo.ContentPanel} The active panel or null
29851      */
29852     getActivePanel : function(){
29853         return this.activePanel;
29854     },
29855     
29856     /**
29857      * Add the passed ContentPanel(s)
29858      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29859      * @return {Roo.ContentPanel} The panel added (if only one was added)
29860      */
29861     add : function(panel){
29862         if(arguments.length > 1){
29863             for(var i = 0, len = arguments.length; i < len; i++) {
29864                 this.add(arguments[i]);
29865             }
29866             return null;
29867         }
29868         if(this.hasPanel(panel)){
29869             this.showPanel(panel);
29870             return panel;
29871         }
29872         var el = panel.getEl();
29873         if(el.dom.parentNode != this.mgr.el.dom){
29874             this.mgr.el.dom.appendChild(el.dom);
29875         }
29876         if(panel.setRegion){
29877             panel.setRegion(this);
29878         }
29879         this.panels.add(panel);
29880         el.setStyle("position", "absolute");
29881         if(!panel.background){
29882             this.setActivePanel(panel);
29883             if(this.config.initialSize && this.panels.getCount()==1){
29884                 this.resizeTo(this.config.initialSize);
29885             }
29886         }
29887         this.fireEvent("paneladded", this, panel);
29888         return panel;
29889     },
29890     
29891     /**
29892      * Returns true if the panel is in this region.
29893      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29894      * @return {Boolean}
29895      */
29896     hasPanel : function(panel){
29897         if(typeof panel == "object"){ // must be panel obj
29898             panel = panel.getId();
29899         }
29900         return this.getPanel(panel) ? true : false;
29901     },
29902     
29903     /**
29904      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29905      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29906      * @param {Boolean} preservePanel Overrides the config preservePanel option
29907      * @return {Roo.ContentPanel} The panel that was removed
29908      */
29909     remove : function(panel, preservePanel){
29910         panel = this.getPanel(panel);
29911         if(!panel){
29912             return null;
29913         }
29914         var e = {};
29915         this.fireEvent("beforeremove", this, panel, e);
29916         if(e.cancel === true){
29917             return null;
29918         }
29919         var panelId = panel.getId();
29920         this.panels.removeKey(panelId);
29921         return panel;
29922     },
29923     
29924     /**
29925      * Returns the panel specified or null if it's not in this region.
29926      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29927      * @return {Roo.ContentPanel}
29928      */
29929     getPanel : function(id){
29930         if(typeof id == "object"){ // must be panel obj
29931             return id;
29932         }
29933         return this.panels.get(id);
29934     },
29935     
29936     /**
29937      * Returns this regions position (north/south/east/west/center).
29938      * @return {String} 
29939      */
29940     getPosition: function(){
29941         return this.position;    
29942     }
29943 });/*
29944  * Based on:
29945  * Ext JS Library 1.1.1
29946  * Copyright(c) 2006-2007, Ext JS, LLC.
29947  *
29948  * Originally Released Under LGPL - original licence link has changed is not relivant.
29949  *
29950  * Fork - LGPL
29951  * <script type="text/javascript">
29952  */
29953  
29954 /**
29955  * @class Roo.LayoutRegion
29956  * @extends Roo.BasicLayoutRegion
29957  * This class represents a region in a layout manager.
29958  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29959  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29960  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29961  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29962  * @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})
29963  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
29964  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29965  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29966  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29967  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29968  * @cfg {String}    title           The title for the region (overrides panel titles)
29969  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29970  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29971  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29972  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29973  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29974  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29975  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29976  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29977  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29978  * @cfg {Boolean}   showPin         True to show a pin button
29979  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29980  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29981  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29982  * @cfg {Number}    width           For East/West panels
29983  * @cfg {Number}    height          For North/South panels
29984  * @cfg {Boolean}   split           To show the splitter
29985  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29986  */
29987 Roo.LayoutRegion = function(mgr, config, pos){
29988     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29989     var dh = Roo.DomHelper;
29990     /** This region's container element 
29991     * @type Roo.Element */
29992     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29993     /** This region's title element 
29994     * @type Roo.Element */
29995
29996     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29997         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29998         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29999     ]}, true);
30000     this.titleEl.enableDisplayMode();
30001     /** This region's title text element 
30002     * @type HTMLElement */
30003     this.titleTextEl = this.titleEl.dom.firstChild;
30004     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30005     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30006     this.closeBtn.enableDisplayMode();
30007     this.closeBtn.on("click", this.closeClicked, this);
30008     this.closeBtn.hide();
30009
30010     this.createBody(config);
30011     this.visible = true;
30012     this.collapsed = false;
30013
30014     if(config.hideWhenEmpty){
30015         this.hide();
30016         this.on("paneladded", this.validateVisibility, this);
30017         this.on("panelremoved", this.validateVisibility, this);
30018     }
30019     this.applyConfig(config);
30020 };
30021
30022 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30023
30024     createBody : function(){
30025         /** This region's body element 
30026         * @type Roo.Element */
30027         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30028     },
30029
30030     applyConfig : function(c){
30031         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30032             var dh = Roo.DomHelper;
30033             if(c.titlebar !== false){
30034                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30035                 this.collapseBtn.on("click", this.collapse, this);
30036                 this.collapseBtn.enableDisplayMode();
30037
30038                 if(c.showPin === true || this.showPin){
30039                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30040                     this.stickBtn.enableDisplayMode();
30041                     this.stickBtn.on("click", this.expand, this);
30042                     this.stickBtn.hide();
30043                 }
30044             }
30045             /** This region's collapsed element
30046             * @type Roo.Element */
30047             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30048                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30049             ]}, true);
30050             if(c.floatable !== false){
30051                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30052                this.collapsedEl.on("click", this.collapseClick, this);
30053             }
30054
30055             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30056                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30057                    id: "message", unselectable: "on", style:{"float":"left"}});
30058                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30059              }
30060             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30061             this.expandBtn.on("click", this.expand, this);
30062         }
30063         if(this.collapseBtn){
30064             this.collapseBtn.setVisible(c.collapsible == true);
30065         }
30066         this.cmargins = c.cmargins || this.cmargins ||
30067                          (this.position == "west" || this.position == "east" ?
30068                              {top: 0, left: 2, right:2, bottom: 0} :
30069                              {top: 2, left: 0, right:0, bottom: 2});
30070         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30071         this.bottomTabs = c.tabPosition != "top";
30072         this.autoScroll = c.autoScroll || false;
30073         if(this.autoScroll){
30074             this.bodyEl.setStyle("overflow", "auto");
30075         }else{
30076             this.bodyEl.setStyle("overflow", "hidden");
30077         }
30078         //if(c.titlebar !== false){
30079             if((!c.titlebar && !c.title) || c.titlebar === false){
30080                 this.titleEl.hide();
30081             }else{
30082                 this.titleEl.show();
30083                 if(c.title){
30084                     this.titleTextEl.innerHTML = c.title;
30085                 }
30086             }
30087         //}
30088         this.duration = c.duration || .30;
30089         this.slideDuration = c.slideDuration || .45;
30090         this.config = c;
30091         if(c.collapsed){
30092             this.collapse(true);
30093         }
30094         if(c.hidden){
30095             this.hide();
30096         }
30097     },
30098     /**
30099      * Returns true if this region is currently visible.
30100      * @return {Boolean}
30101      */
30102     isVisible : function(){
30103         return this.visible;
30104     },
30105
30106     /**
30107      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30108      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30109      */
30110     setCollapsedTitle : function(title){
30111         title = title || "&#160;";
30112         if(this.collapsedTitleTextEl){
30113             this.collapsedTitleTextEl.innerHTML = title;
30114         }
30115     },
30116
30117     getBox : function(){
30118         var b;
30119         if(!this.collapsed){
30120             b = this.el.getBox(false, true);
30121         }else{
30122             b = this.collapsedEl.getBox(false, true);
30123         }
30124         return b;
30125     },
30126
30127     getMargins : function(){
30128         return this.collapsed ? this.cmargins : this.margins;
30129     },
30130
30131     highlight : function(){
30132         this.el.addClass("x-layout-panel-dragover");
30133     },
30134
30135     unhighlight : function(){
30136         this.el.removeClass("x-layout-panel-dragover");
30137     },
30138
30139     updateBox : function(box){
30140         this.box = box;
30141         if(!this.collapsed){
30142             this.el.dom.style.left = box.x + "px";
30143             this.el.dom.style.top = box.y + "px";
30144             this.updateBody(box.width, box.height);
30145         }else{
30146             this.collapsedEl.dom.style.left = box.x + "px";
30147             this.collapsedEl.dom.style.top = box.y + "px";
30148             this.collapsedEl.setSize(box.width, box.height);
30149         }
30150         if(this.tabs){
30151             this.tabs.autoSizeTabs();
30152         }
30153     },
30154
30155     updateBody : function(w, h){
30156         if(w !== null){
30157             this.el.setWidth(w);
30158             w -= this.el.getBorderWidth("rl");
30159             if(this.config.adjustments){
30160                 w += this.config.adjustments[0];
30161             }
30162         }
30163         if(h !== null){
30164             this.el.setHeight(h);
30165             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30166             h -= this.el.getBorderWidth("tb");
30167             if(this.config.adjustments){
30168                 h += this.config.adjustments[1];
30169             }
30170             this.bodyEl.setHeight(h);
30171             if(this.tabs){
30172                 h = this.tabs.syncHeight(h);
30173             }
30174         }
30175         if(this.panelSize){
30176             w = w !== null ? w : this.panelSize.width;
30177             h = h !== null ? h : this.panelSize.height;
30178         }
30179         if(this.activePanel){
30180             var el = this.activePanel.getEl();
30181             w = w !== null ? w : el.getWidth();
30182             h = h !== null ? h : el.getHeight();
30183             this.panelSize = {width: w, height: h};
30184             this.activePanel.setSize(w, h);
30185         }
30186         if(Roo.isIE && this.tabs){
30187             this.tabs.el.repaint();
30188         }
30189     },
30190
30191     /**
30192      * Returns the container element for this region.
30193      * @return {Roo.Element}
30194      */
30195     getEl : function(){
30196         return this.el;
30197     },
30198
30199     /**
30200      * Hides this region.
30201      */
30202     hide : function(){
30203         if(!this.collapsed){
30204             this.el.dom.style.left = "-2000px";
30205             this.el.hide();
30206         }else{
30207             this.collapsedEl.dom.style.left = "-2000px";
30208             this.collapsedEl.hide();
30209         }
30210         this.visible = false;
30211         this.fireEvent("visibilitychange", this, false);
30212     },
30213
30214     /**
30215      * Shows this region if it was previously hidden.
30216      */
30217     show : function(){
30218         if(!this.collapsed){
30219             this.el.show();
30220         }else{
30221             this.collapsedEl.show();
30222         }
30223         this.visible = true;
30224         this.fireEvent("visibilitychange", this, true);
30225     },
30226
30227     closeClicked : function(){
30228         if(this.activePanel){
30229             this.remove(this.activePanel);
30230         }
30231     },
30232
30233     collapseClick : function(e){
30234         if(this.isSlid){
30235            e.stopPropagation();
30236            this.slideIn();
30237         }else{
30238            e.stopPropagation();
30239            this.slideOut();
30240         }
30241     },
30242
30243     /**
30244      * Collapses this region.
30245      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30246      */
30247     collapse : function(skipAnim, skipCheck){
30248         if(this.collapsed) {
30249             return;
30250         }
30251         
30252         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30253             
30254             this.collapsed = true;
30255             if(this.split){
30256                 this.split.el.hide();
30257             }
30258             if(this.config.animate && skipAnim !== true){
30259                 this.fireEvent("invalidated", this);
30260                 this.animateCollapse();
30261             }else{
30262                 this.el.setLocation(-20000,-20000);
30263                 this.el.hide();
30264                 this.collapsedEl.show();
30265                 this.fireEvent("collapsed", this);
30266                 this.fireEvent("invalidated", this);
30267             }
30268         }
30269         
30270     },
30271
30272     animateCollapse : function(){
30273         // overridden
30274     },
30275
30276     /**
30277      * Expands this region if it was previously collapsed.
30278      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30279      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30280      */
30281     expand : function(e, skipAnim){
30282         if(e) {
30283             e.stopPropagation();
30284         }
30285         if(!this.collapsed || this.el.hasActiveFx()) {
30286             return;
30287         }
30288         if(this.isSlid){
30289             this.afterSlideIn();
30290             skipAnim = true;
30291         }
30292         this.collapsed = false;
30293         if(this.config.animate && skipAnim !== true){
30294             this.animateExpand();
30295         }else{
30296             this.el.show();
30297             if(this.split){
30298                 this.split.el.show();
30299             }
30300             this.collapsedEl.setLocation(-2000,-2000);
30301             this.collapsedEl.hide();
30302             this.fireEvent("invalidated", this);
30303             this.fireEvent("expanded", this);
30304         }
30305     },
30306
30307     animateExpand : function(){
30308         // overridden
30309     },
30310
30311     initTabs : function()
30312     {
30313         this.bodyEl.setStyle("overflow", "hidden");
30314         var ts = new Roo.TabPanel(
30315                 this.bodyEl.dom,
30316                 {
30317                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30318                     disableTooltips: this.config.disableTabTips,
30319                     toolbar : this.config.toolbar
30320                 }
30321         );
30322         if(this.config.hideTabs){
30323             ts.stripWrap.setDisplayed(false);
30324         }
30325         this.tabs = ts;
30326         ts.resizeTabs = this.config.resizeTabs === true;
30327         ts.minTabWidth = this.config.minTabWidth || 40;
30328         ts.maxTabWidth = this.config.maxTabWidth || 250;
30329         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30330         ts.monitorResize = false;
30331         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30332         ts.bodyEl.addClass('x-layout-tabs-body');
30333         this.panels.each(this.initPanelAsTab, this);
30334     },
30335
30336     initPanelAsTab : function(panel){
30337         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30338                     this.config.closeOnTab && panel.isClosable());
30339         if(panel.tabTip !== undefined){
30340             ti.setTooltip(panel.tabTip);
30341         }
30342         ti.on("activate", function(){
30343               this.setActivePanel(panel);
30344         }, this);
30345         if(this.config.closeOnTab){
30346             ti.on("beforeclose", function(t, e){
30347                 e.cancel = true;
30348                 this.remove(panel);
30349             }, this);
30350         }
30351         return ti;
30352     },
30353
30354     updatePanelTitle : function(panel, title){
30355         if(this.activePanel == panel){
30356             this.updateTitle(title);
30357         }
30358         if(this.tabs){
30359             var ti = this.tabs.getTab(panel.getEl().id);
30360             ti.setText(title);
30361             if(panel.tabTip !== undefined){
30362                 ti.setTooltip(panel.tabTip);
30363             }
30364         }
30365     },
30366
30367     updateTitle : function(title){
30368         if(this.titleTextEl && !this.config.title){
30369             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30370         }
30371     },
30372
30373     setActivePanel : function(panel){
30374         panel = this.getPanel(panel);
30375         if(this.activePanel && this.activePanel != panel){
30376             this.activePanel.setActiveState(false);
30377         }
30378         this.activePanel = panel;
30379         panel.setActiveState(true);
30380         if(this.panelSize){
30381             panel.setSize(this.panelSize.width, this.panelSize.height);
30382         }
30383         if(this.closeBtn){
30384             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30385         }
30386         this.updateTitle(panel.getTitle());
30387         if(this.tabs){
30388             this.fireEvent("invalidated", this);
30389         }
30390         this.fireEvent("panelactivated", this, panel);
30391     },
30392
30393     /**
30394      * Shows the specified panel.
30395      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30396      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30397      */
30398     showPanel : function(panel)
30399     {
30400         panel = this.getPanel(panel);
30401         if(panel){
30402             if(this.tabs){
30403                 var tab = this.tabs.getTab(panel.getEl().id);
30404                 if(tab.isHidden()){
30405                     this.tabs.unhideTab(tab.id);
30406                 }
30407                 tab.activate();
30408             }else{
30409                 this.setActivePanel(panel);
30410             }
30411         }
30412         return panel;
30413     },
30414
30415     /**
30416      * Get the active panel for this region.
30417      * @return {Roo.ContentPanel} The active panel or null
30418      */
30419     getActivePanel : function(){
30420         return this.activePanel;
30421     },
30422
30423     validateVisibility : function(){
30424         if(this.panels.getCount() < 1){
30425             this.updateTitle("&#160;");
30426             this.closeBtn.hide();
30427             this.hide();
30428         }else{
30429             if(!this.isVisible()){
30430                 this.show();
30431             }
30432         }
30433     },
30434
30435     /**
30436      * Adds the passed ContentPanel(s) to this region.
30437      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30438      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30439      */
30440     add : function(panel){
30441         if(arguments.length > 1){
30442             for(var i = 0, len = arguments.length; i < len; i++) {
30443                 this.add(arguments[i]);
30444             }
30445             return null;
30446         }
30447         if(this.hasPanel(panel)){
30448             this.showPanel(panel);
30449             return panel;
30450         }
30451         panel.setRegion(this);
30452         this.panels.add(panel);
30453         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30454             this.bodyEl.dom.appendChild(panel.getEl().dom);
30455             if(panel.background !== true){
30456                 this.setActivePanel(panel);
30457             }
30458             this.fireEvent("paneladded", this, panel);
30459             return panel;
30460         }
30461         if(!this.tabs){
30462             this.initTabs();
30463         }else{
30464             this.initPanelAsTab(panel);
30465         }
30466         if(panel.background !== true){
30467             this.tabs.activate(panel.getEl().id);
30468         }
30469         this.fireEvent("paneladded", this, panel);
30470         return panel;
30471     },
30472
30473     /**
30474      * Hides the tab for the specified panel.
30475      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30476      */
30477     hidePanel : function(panel){
30478         if(this.tabs && (panel = this.getPanel(panel))){
30479             this.tabs.hideTab(panel.getEl().id);
30480         }
30481     },
30482
30483     /**
30484      * Unhides the tab for a previously hidden panel.
30485      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30486      */
30487     unhidePanel : function(panel){
30488         if(this.tabs && (panel = this.getPanel(panel))){
30489             this.tabs.unhideTab(panel.getEl().id);
30490         }
30491     },
30492
30493     clearPanels : function(){
30494         while(this.panels.getCount() > 0){
30495              this.remove(this.panels.first());
30496         }
30497     },
30498
30499     /**
30500      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30501      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30502      * @param {Boolean} preservePanel Overrides the config preservePanel option
30503      * @return {Roo.ContentPanel} The panel that was removed
30504      */
30505     remove : function(panel, preservePanel){
30506         panel = this.getPanel(panel);
30507         if(!panel){
30508             return null;
30509         }
30510         var e = {};
30511         this.fireEvent("beforeremove", this, panel, e);
30512         if(e.cancel === true){
30513             return null;
30514         }
30515         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30516         var panelId = panel.getId();
30517         this.panels.removeKey(panelId);
30518         if(preservePanel){
30519             document.body.appendChild(panel.getEl().dom);
30520         }
30521         if(this.tabs){
30522             this.tabs.removeTab(panel.getEl().id);
30523         }else if (!preservePanel){
30524             this.bodyEl.dom.removeChild(panel.getEl().dom);
30525         }
30526         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30527             var p = this.panels.first();
30528             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30529             tempEl.appendChild(p.getEl().dom);
30530             this.bodyEl.update("");
30531             this.bodyEl.dom.appendChild(p.getEl().dom);
30532             tempEl = null;
30533             this.updateTitle(p.getTitle());
30534             this.tabs = null;
30535             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30536             this.setActivePanel(p);
30537         }
30538         panel.setRegion(null);
30539         if(this.activePanel == panel){
30540             this.activePanel = null;
30541         }
30542         if(this.config.autoDestroy !== false && preservePanel !== true){
30543             try{panel.destroy();}catch(e){}
30544         }
30545         this.fireEvent("panelremoved", this, panel);
30546         return panel;
30547     },
30548
30549     /**
30550      * Returns the TabPanel component used by this region
30551      * @return {Roo.TabPanel}
30552      */
30553     getTabs : function(){
30554         return this.tabs;
30555     },
30556
30557     createTool : function(parentEl, className){
30558         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30559             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30560         btn.addClassOnOver("x-layout-tools-button-over");
30561         return btn;
30562     }
30563 });/*
30564  * Based on:
30565  * Ext JS Library 1.1.1
30566  * Copyright(c) 2006-2007, Ext JS, LLC.
30567  *
30568  * Originally Released Under LGPL - original licence link has changed is not relivant.
30569  *
30570  * Fork - LGPL
30571  * <script type="text/javascript">
30572  */
30573  
30574
30575
30576 /**
30577  * @class Roo.SplitLayoutRegion
30578  * @extends Roo.LayoutRegion
30579  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30580  */
30581 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30582     this.cursor = cursor;
30583     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30584 };
30585
30586 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30587     splitTip : "Drag to resize.",
30588     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30589     useSplitTips : false,
30590
30591     applyConfig : function(config){
30592         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30593         if(config.split){
30594             if(!this.split){
30595                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30596                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30597                 /** The SplitBar for this region 
30598                 * @type Roo.SplitBar */
30599                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30600                 this.split.on("moved", this.onSplitMove, this);
30601                 this.split.useShim = config.useShim === true;
30602                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30603                 if(this.useSplitTips){
30604                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30605                 }
30606                 if(config.collapsible){
30607                     this.split.el.on("dblclick", this.collapse,  this);
30608                 }
30609             }
30610             if(typeof config.minSize != "undefined"){
30611                 this.split.minSize = config.minSize;
30612             }
30613             if(typeof config.maxSize != "undefined"){
30614                 this.split.maxSize = config.maxSize;
30615             }
30616             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30617                 this.hideSplitter();
30618             }
30619         }
30620     },
30621
30622     getHMaxSize : function(){
30623          var cmax = this.config.maxSize || 10000;
30624          var center = this.mgr.getRegion("center");
30625          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30626     },
30627
30628     getVMaxSize : function(){
30629          var cmax = this.config.maxSize || 10000;
30630          var center = this.mgr.getRegion("center");
30631          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30632     },
30633
30634     onSplitMove : function(split, newSize){
30635         this.fireEvent("resized", this, newSize);
30636     },
30637     
30638     /** 
30639      * Returns the {@link Roo.SplitBar} for this region.
30640      * @return {Roo.SplitBar}
30641      */
30642     getSplitBar : function(){
30643         return this.split;
30644     },
30645     
30646     hide : function(){
30647         this.hideSplitter();
30648         Roo.SplitLayoutRegion.superclass.hide.call(this);
30649     },
30650
30651     hideSplitter : function(){
30652         if(this.split){
30653             this.split.el.setLocation(-2000,-2000);
30654             this.split.el.hide();
30655         }
30656     },
30657
30658     show : function(){
30659         if(this.split){
30660             this.split.el.show();
30661         }
30662         Roo.SplitLayoutRegion.superclass.show.call(this);
30663     },
30664     
30665     beforeSlide: function(){
30666         if(Roo.isGecko){// firefox overflow auto bug workaround
30667             this.bodyEl.clip();
30668             if(this.tabs) {
30669                 this.tabs.bodyEl.clip();
30670             }
30671             if(this.activePanel){
30672                 this.activePanel.getEl().clip();
30673                 
30674                 if(this.activePanel.beforeSlide){
30675                     this.activePanel.beforeSlide();
30676                 }
30677             }
30678         }
30679     },
30680     
30681     afterSlide : function(){
30682         if(Roo.isGecko){// firefox overflow auto bug workaround
30683             this.bodyEl.unclip();
30684             if(this.tabs) {
30685                 this.tabs.bodyEl.unclip();
30686             }
30687             if(this.activePanel){
30688                 this.activePanel.getEl().unclip();
30689                 if(this.activePanel.afterSlide){
30690                     this.activePanel.afterSlide();
30691                 }
30692             }
30693         }
30694     },
30695
30696     initAutoHide : function(){
30697         if(this.autoHide !== false){
30698             if(!this.autoHideHd){
30699                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30700                 this.autoHideHd = {
30701                     "mouseout": function(e){
30702                         if(!e.within(this.el, true)){
30703                             st.delay(500);
30704                         }
30705                     },
30706                     "mouseover" : function(e){
30707                         st.cancel();
30708                     },
30709                     scope : this
30710                 };
30711             }
30712             this.el.on(this.autoHideHd);
30713         }
30714     },
30715
30716     clearAutoHide : function(){
30717         if(this.autoHide !== false){
30718             this.el.un("mouseout", this.autoHideHd.mouseout);
30719             this.el.un("mouseover", this.autoHideHd.mouseover);
30720         }
30721     },
30722
30723     clearMonitor : function(){
30724         Roo.get(document).un("click", this.slideInIf, this);
30725     },
30726
30727     // these names are backwards but not changed for compat
30728     slideOut : function(){
30729         if(this.isSlid || this.el.hasActiveFx()){
30730             return;
30731         }
30732         this.isSlid = true;
30733         if(this.collapseBtn){
30734             this.collapseBtn.hide();
30735         }
30736         this.closeBtnState = this.closeBtn.getStyle('display');
30737         this.closeBtn.hide();
30738         if(this.stickBtn){
30739             this.stickBtn.show();
30740         }
30741         this.el.show();
30742         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30743         this.beforeSlide();
30744         this.el.setStyle("z-index", 10001);
30745         this.el.slideIn(this.getSlideAnchor(), {
30746             callback: function(){
30747                 this.afterSlide();
30748                 this.initAutoHide();
30749                 Roo.get(document).on("click", this.slideInIf, this);
30750                 this.fireEvent("slideshow", this);
30751             },
30752             scope: this,
30753             block: true
30754         });
30755     },
30756
30757     afterSlideIn : function(){
30758         this.clearAutoHide();
30759         this.isSlid = false;
30760         this.clearMonitor();
30761         this.el.setStyle("z-index", "");
30762         if(this.collapseBtn){
30763             this.collapseBtn.show();
30764         }
30765         this.closeBtn.setStyle('display', this.closeBtnState);
30766         if(this.stickBtn){
30767             this.stickBtn.hide();
30768         }
30769         this.fireEvent("slidehide", this);
30770     },
30771
30772     slideIn : function(cb){
30773         if(!this.isSlid || this.el.hasActiveFx()){
30774             Roo.callback(cb);
30775             return;
30776         }
30777         this.isSlid = false;
30778         this.beforeSlide();
30779         this.el.slideOut(this.getSlideAnchor(), {
30780             callback: function(){
30781                 this.el.setLeftTop(-10000, -10000);
30782                 this.afterSlide();
30783                 this.afterSlideIn();
30784                 Roo.callback(cb);
30785             },
30786             scope: this,
30787             block: true
30788         });
30789     },
30790     
30791     slideInIf : function(e){
30792         if(!e.within(this.el)){
30793             this.slideIn();
30794         }
30795     },
30796
30797     animateCollapse : function(){
30798         this.beforeSlide();
30799         this.el.setStyle("z-index", 20000);
30800         var anchor = this.getSlideAnchor();
30801         this.el.slideOut(anchor, {
30802             callback : function(){
30803                 this.el.setStyle("z-index", "");
30804                 this.collapsedEl.slideIn(anchor, {duration:.3});
30805                 this.afterSlide();
30806                 this.el.setLocation(-10000,-10000);
30807                 this.el.hide();
30808                 this.fireEvent("collapsed", this);
30809             },
30810             scope: this,
30811             block: true
30812         });
30813     },
30814
30815     animateExpand : function(){
30816         this.beforeSlide();
30817         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30818         this.el.setStyle("z-index", 20000);
30819         this.collapsedEl.hide({
30820             duration:.1
30821         });
30822         this.el.slideIn(this.getSlideAnchor(), {
30823             callback : function(){
30824                 this.el.setStyle("z-index", "");
30825                 this.afterSlide();
30826                 if(this.split){
30827                     this.split.el.show();
30828                 }
30829                 this.fireEvent("invalidated", this);
30830                 this.fireEvent("expanded", this);
30831             },
30832             scope: this,
30833             block: true
30834         });
30835     },
30836
30837     anchors : {
30838         "west" : "left",
30839         "east" : "right",
30840         "north" : "top",
30841         "south" : "bottom"
30842     },
30843
30844     sanchors : {
30845         "west" : "l",
30846         "east" : "r",
30847         "north" : "t",
30848         "south" : "b"
30849     },
30850
30851     canchors : {
30852         "west" : "tl-tr",
30853         "east" : "tr-tl",
30854         "north" : "tl-bl",
30855         "south" : "bl-tl"
30856     },
30857
30858     getAnchor : function(){
30859         return this.anchors[this.position];
30860     },
30861
30862     getCollapseAnchor : function(){
30863         return this.canchors[this.position];
30864     },
30865
30866     getSlideAnchor : function(){
30867         return this.sanchors[this.position];
30868     },
30869
30870     getAlignAdj : function(){
30871         var cm = this.cmargins;
30872         switch(this.position){
30873             case "west":
30874                 return [0, 0];
30875             break;
30876             case "east":
30877                 return [0, 0];
30878             break;
30879             case "north":
30880                 return [0, 0];
30881             break;
30882             case "south":
30883                 return [0, 0];
30884             break;
30885         }
30886     },
30887
30888     getExpandAdj : function(){
30889         var c = this.collapsedEl, cm = this.cmargins;
30890         switch(this.position){
30891             case "west":
30892                 return [-(cm.right+c.getWidth()+cm.left), 0];
30893             break;
30894             case "east":
30895                 return [cm.right+c.getWidth()+cm.left, 0];
30896             break;
30897             case "north":
30898                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30899             break;
30900             case "south":
30901                 return [0, cm.top+cm.bottom+c.getHeight()];
30902             break;
30903         }
30904     }
30905 });/*
30906  * Based on:
30907  * Ext JS Library 1.1.1
30908  * Copyright(c) 2006-2007, Ext JS, LLC.
30909  *
30910  * Originally Released Under LGPL - original licence link has changed is not relivant.
30911  *
30912  * Fork - LGPL
30913  * <script type="text/javascript">
30914  */
30915 /*
30916  * These classes are private internal classes
30917  */
30918 Roo.CenterLayoutRegion = function(mgr, config){
30919     Roo.LayoutRegion.call(this, mgr, config, "center");
30920     this.visible = true;
30921     this.minWidth = config.minWidth || 20;
30922     this.minHeight = config.minHeight || 20;
30923 };
30924
30925 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30926     hide : function(){
30927         // center panel can't be hidden
30928     },
30929     
30930     show : function(){
30931         // center panel can't be hidden
30932     },
30933     
30934     getMinWidth: function(){
30935         return this.minWidth;
30936     },
30937     
30938     getMinHeight: function(){
30939         return this.minHeight;
30940     }
30941 });
30942
30943
30944 Roo.NorthLayoutRegion = function(mgr, config){
30945     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30946     if(this.split){
30947         this.split.placement = Roo.SplitBar.TOP;
30948         this.split.orientation = Roo.SplitBar.VERTICAL;
30949         this.split.el.addClass("x-layout-split-v");
30950     }
30951     var size = config.initialSize || config.height;
30952     if(typeof size != "undefined"){
30953         this.el.setHeight(size);
30954     }
30955 };
30956 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30957     orientation: Roo.SplitBar.VERTICAL,
30958     getBox : function(){
30959         if(this.collapsed){
30960             return this.collapsedEl.getBox();
30961         }
30962         var box = this.el.getBox();
30963         if(this.split){
30964             box.height += this.split.el.getHeight();
30965         }
30966         return box;
30967     },
30968     
30969     updateBox : function(box){
30970         if(this.split && !this.collapsed){
30971             box.height -= this.split.el.getHeight();
30972             this.split.el.setLeft(box.x);
30973             this.split.el.setTop(box.y+box.height);
30974             this.split.el.setWidth(box.width);
30975         }
30976         if(this.collapsed){
30977             this.updateBody(box.width, null);
30978         }
30979         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30980     }
30981 });
30982
30983 Roo.SouthLayoutRegion = function(mgr, config){
30984     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30985     if(this.split){
30986         this.split.placement = Roo.SplitBar.BOTTOM;
30987         this.split.orientation = Roo.SplitBar.VERTICAL;
30988         this.split.el.addClass("x-layout-split-v");
30989     }
30990     var size = config.initialSize || config.height;
30991     if(typeof size != "undefined"){
30992         this.el.setHeight(size);
30993     }
30994 };
30995 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30996     orientation: Roo.SplitBar.VERTICAL,
30997     getBox : function(){
30998         if(this.collapsed){
30999             return this.collapsedEl.getBox();
31000         }
31001         var box = this.el.getBox();
31002         if(this.split){
31003             var sh = this.split.el.getHeight();
31004             box.height += sh;
31005             box.y -= sh;
31006         }
31007         return box;
31008     },
31009     
31010     updateBox : function(box){
31011         if(this.split && !this.collapsed){
31012             var sh = this.split.el.getHeight();
31013             box.height -= sh;
31014             box.y += sh;
31015             this.split.el.setLeft(box.x);
31016             this.split.el.setTop(box.y-sh);
31017             this.split.el.setWidth(box.width);
31018         }
31019         if(this.collapsed){
31020             this.updateBody(box.width, null);
31021         }
31022         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31023     }
31024 });
31025
31026 Roo.EastLayoutRegion = function(mgr, config){
31027     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31028     if(this.split){
31029         this.split.placement = Roo.SplitBar.RIGHT;
31030         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31031         this.split.el.addClass("x-layout-split-h");
31032     }
31033     var size = config.initialSize || config.width;
31034     if(typeof size != "undefined"){
31035         this.el.setWidth(size);
31036     }
31037 };
31038 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31039     orientation: Roo.SplitBar.HORIZONTAL,
31040     getBox : function(){
31041         if(this.collapsed){
31042             return this.collapsedEl.getBox();
31043         }
31044         var box = this.el.getBox();
31045         if(this.split){
31046             var sw = this.split.el.getWidth();
31047             box.width += sw;
31048             box.x -= sw;
31049         }
31050         return box;
31051     },
31052
31053     updateBox : function(box){
31054         if(this.split && !this.collapsed){
31055             var sw = this.split.el.getWidth();
31056             box.width -= sw;
31057             this.split.el.setLeft(box.x);
31058             this.split.el.setTop(box.y);
31059             this.split.el.setHeight(box.height);
31060             box.x += sw;
31061         }
31062         if(this.collapsed){
31063             this.updateBody(null, box.height);
31064         }
31065         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31066     }
31067 });
31068
31069 Roo.WestLayoutRegion = function(mgr, config){
31070     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31071     if(this.split){
31072         this.split.placement = Roo.SplitBar.LEFT;
31073         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31074         this.split.el.addClass("x-layout-split-h");
31075     }
31076     var size = config.initialSize || config.width;
31077     if(typeof size != "undefined"){
31078         this.el.setWidth(size);
31079     }
31080 };
31081 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31082     orientation: Roo.SplitBar.HORIZONTAL,
31083     getBox : function(){
31084         if(this.collapsed){
31085             return this.collapsedEl.getBox();
31086         }
31087         var box = this.el.getBox();
31088         if(this.split){
31089             box.width += this.split.el.getWidth();
31090         }
31091         return box;
31092     },
31093     
31094     updateBox : function(box){
31095         if(this.split && !this.collapsed){
31096             var sw = this.split.el.getWidth();
31097             box.width -= sw;
31098             this.split.el.setLeft(box.x+box.width);
31099             this.split.el.setTop(box.y);
31100             this.split.el.setHeight(box.height);
31101         }
31102         if(this.collapsed){
31103             this.updateBody(null, box.height);
31104         }
31105         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31106     }
31107 });
31108 /*
31109  * Based on:
31110  * Ext JS Library 1.1.1
31111  * Copyright(c) 2006-2007, Ext JS, LLC.
31112  *
31113  * Originally Released Under LGPL - original licence link has changed is not relivant.
31114  *
31115  * Fork - LGPL
31116  * <script type="text/javascript">
31117  */
31118  
31119  
31120 /*
31121  * Private internal class for reading and applying state
31122  */
31123 Roo.LayoutStateManager = function(layout){
31124      // default empty state
31125      this.state = {
31126         north: {},
31127         south: {},
31128         east: {},
31129         west: {}       
31130     };
31131 };
31132
31133 Roo.LayoutStateManager.prototype = {
31134     init : function(layout, provider){
31135         this.provider = provider;
31136         var state = provider.get(layout.id+"-layout-state");
31137         if(state){
31138             var wasUpdating = layout.isUpdating();
31139             if(!wasUpdating){
31140                 layout.beginUpdate();
31141             }
31142             for(var key in state){
31143                 if(typeof state[key] != "function"){
31144                     var rstate = state[key];
31145                     var r = layout.getRegion(key);
31146                     if(r && rstate){
31147                         if(rstate.size){
31148                             r.resizeTo(rstate.size);
31149                         }
31150                         if(rstate.collapsed == true){
31151                             r.collapse(true);
31152                         }else{
31153                             r.expand(null, true);
31154                         }
31155                     }
31156                 }
31157             }
31158             if(!wasUpdating){
31159                 layout.endUpdate();
31160             }
31161             this.state = state; 
31162         }
31163         this.layout = layout;
31164         layout.on("regionresized", this.onRegionResized, this);
31165         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31166         layout.on("regionexpanded", this.onRegionExpanded, this);
31167     },
31168     
31169     storeState : function(){
31170         this.provider.set(this.layout.id+"-layout-state", this.state);
31171     },
31172     
31173     onRegionResized : function(region, newSize){
31174         this.state[region.getPosition()].size = newSize;
31175         this.storeState();
31176     },
31177     
31178     onRegionCollapsed : function(region){
31179         this.state[region.getPosition()].collapsed = true;
31180         this.storeState();
31181     },
31182     
31183     onRegionExpanded : function(region){
31184         this.state[region.getPosition()].collapsed = false;
31185         this.storeState();
31186     }
31187 };/*
31188  * Based on:
31189  * Ext JS Library 1.1.1
31190  * Copyright(c) 2006-2007, Ext JS, LLC.
31191  *
31192  * Originally Released Under LGPL - original licence link has changed is not relivant.
31193  *
31194  * Fork - LGPL
31195  * <script type="text/javascript">
31196  */
31197 /**
31198  * @class Roo.ContentPanel
31199  * @extends Roo.util.Observable
31200  * A basic ContentPanel element.
31201  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31202  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31203  * @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
31204  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31205  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31206  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31207  * @cfg {Toolbar}   toolbar       A toolbar for this panel
31208  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31209  * @cfg {String} title          The title for this panel
31210  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31211  * @cfg {String} url            Calls {@link #setUrl} with this value
31212  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31213  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31214  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31215  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31216  * @cfg {String}    style  Extra style to add to the content panel 
31217
31218  * @constructor
31219  * Create a new ContentPanel.
31220  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31221  * @param {String/Object} config A string to set only the title or a config object
31222  * @param {String} content (optional) Set the HTML content for this panel
31223  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31224  */
31225 Roo.ContentPanel = function(el, config, content){
31226     
31227      
31228     /*
31229     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31230         config = el;
31231         el = Roo.id();
31232     }
31233     if (config && config.parentLayout) { 
31234         el = config.parentLayout.el.createChild(); 
31235     }
31236     */
31237     if(el.autoCreate){ // xtype is available if this is called from factory
31238         config = el;
31239         el = Roo.id();
31240     }
31241     this.el = Roo.get(el);
31242     if(!this.el && config && config.autoCreate){
31243         if(typeof config.autoCreate == "object"){
31244             if(!config.autoCreate.id){
31245                 config.autoCreate.id = config.id||el;
31246             }
31247             this.el = Roo.DomHelper.append(document.body,
31248                         config.autoCreate, true);
31249         }else{
31250             this.el = Roo.DomHelper.append(document.body,
31251                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31252         }
31253     }
31254     
31255     
31256     this.closable = false;
31257     this.loaded = false;
31258     this.active = false;
31259     if(typeof config == "string"){
31260         this.title = config;
31261     }else{
31262         Roo.apply(this, config);
31263     }
31264     
31265     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31266         this.wrapEl = this.el.wrap();
31267         this.toolbar.container = this.el.insertSibling(false, 'before');
31268         this.toolbar = new Roo.Toolbar(this.toolbar);
31269     }
31270     
31271     // xtype created footer. - not sure if will work as we normally have to render first..
31272     if (this.footer && !this.footer.el && this.footer.xtype) {
31273         if (!this.wrapEl) {
31274             this.wrapEl = this.el.wrap();
31275         }
31276     
31277         this.footer.container = this.wrapEl.createChild();
31278          
31279         this.footer = Roo.factory(this.footer, Roo);
31280         
31281     }
31282     
31283     if(this.resizeEl){
31284         this.resizeEl = Roo.get(this.resizeEl, true);
31285     }else{
31286         this.resizeEl = this.el;
31287     }
31288     // handle view.xtype
31289     
31290  
31291     
31292     
31293     this.addEvents({
31294         /**
31295          * @event activate
31296          * Fires when this panel is activated. 
31297          * @param {Roo.ContentPanel} this
31298          */
31299         "activate" : true,
31300         /**
31301          * @event deactivate
31302          * Fires when this panel is activated. 
31303          * @param {Roo.ContentPanel} this
31304          */
31305         "deactivate" : true,
31306
31307         /**
31308          * @event resize
31309          * Fires when this panel is resized if fitToFrame is true.
31310          * @param {Roo.ContentPanel} this
31311          * @param {Number} width The width after any component adjustments
31312          * @param {Number} height The height after any component adjustments
31313          */
31314         "resize" : true,
31315         
31316          /**
31317          * @event render
31318          * Fires when this tab is created
31319          * @param {Roo.ContentPanel} this
31320          */
31321         "render" : true
31322          
31323         
31324     });
31325     
31326
31327     
31328     
31329     if(this.autoScroll){
31330         this.resizeEl.setStyle("overflow", "auto");
31331     } else {
31332         // fix randome scrolling
31333         this.el.on('scroll', function() {
31334             Roo.log('fix random scolling');
31335             this.scrollTo('top',0); 
31336         });
31337     }
31338     content = content || this.content;
31339     if(content){
31340         this.setContent(content);
31341     }
31342     if(config && config.url){
31343         this.setUrl(this.url, this.params, this.loadOnce);
31344     }
31345     
31346     
31347     
31348     Roo.ContentPanel.superclass.constructor.call(this);
31349     
31350     if (this.view && typeof(this.view.xtype) != 'undefined') {
31351         this.view.el = this.el.appendChild(document.createElement("div"));
31352         this.view = Roo.factory(this.view); 
31353         this.view.render  &&  this.view.render(false, '');  
31354     }
31355     
31356     
31357     this.fireEvent('render', this);
31358 };
31359
31360 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31361     tabTip:'',
31362     setRegion : function(region){
31363         this.region = region;
31364         if(region){
31365            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31366         }else{
31367            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31368         } 
31369     },
31370     
31371     /**
31372      * Returns the toolbar for this Panel if one was configured. 
31373      * @return {Roo.Toolbar} 
31374      */
31375     getToolbar : function(){
31376         return this.toolbar;
31377     },
31378     
31379     setActiveState : function(active){
31380         this.active = active;
31381         if(!active){
31382             this.fireEvent("deactivate", this);
31383         }else{
31384             this.fireEvent("activate", this);
31385         }
31386     },
31387     /**
31388      * Updates this panel's element
31389      * @param {String} content The new content
31390      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31391     */
31392     setContent : function(content, loadScripts){
31393         this.el.update(content, loadScripts);
31394     },
31395
31396     ignoreResize : function(w, h){
31397         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31398             return true;
31399         }else{
31400             this.lastSize = {width: w, height: h};
31401             return false;
31402         }
31403     },
31404     /**
31405      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31406      * @return {Roo.UpdateManager} The UpdateManager
31407      */
31408     getUpdateManager : function(){
31409         return this.el.getUpdateManager();
31410     },
31411      /**
31412      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31413      * @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:
31414 <pre><code>
31415 panel.load({
31416     url: "your-url.php",
31417     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31418     callback: yourFunction,
31419     scope: yourObject, //(optional scope)
31420     discardUrl: false,
31421     nocache: false,
31422     text: "Loading...",
31423     timeout: 30,
31424     scripts: false
31425 });
31426 </code></pre>
31427      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31428      * 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.
31429      * @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}
31430      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31431      * @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.
31432      * @return {Roo.ContentPanel} this
31433      */
31434     load : function(){
31435         var um = this.el.getUpdateManager();
31436         um.update.apply(um, arguments);
31437         return this;
31438     },
31439
31440
31441     /**
31442      * 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.
31443      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31444      * @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)
31445      * @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)
31446      * @return {Roo.UpdateManager} The UpdateManager
31447      */
31448     setUrl : function(url, params, loadOnce){
31449         if(this.refreshDelegate){
31450             this.removeListener("activate", this.refreshDelegate);
31451         }
31452         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31453         this.on("activate", this.refreshDelegate);
31454         return this.el.getUpdateManager();
31455     },
31456     
31457     _handleRefresh : function(url, params, loadOnce){
31458         if(!loadOnce || !this.loaded){
31459             var updater = this.el.getUpdateManager();
31460             updater.update(url, params, this._setLoaded.createDelegate(this));
31461         }
31462     },
31463     
31464     _setLoaded : function(){
31465         this.loaded = true;
31466     }, 
31467     
31468     /**
31469      * Returns this panel's id
31470      * @return {String} 
31471      */
31472     getId : function(){
31473         return this.el.id;
31474     },
31475     
31476     /** 
31477      * Returns this panel's element - used by regiosn to add.
31478      * @return {Roo.Element} 
31479      */
31480     getEl : function(){
31481         return this.wrapEl || this.el;
31482     },
31483     
31484     adjustForComponents : function(width, height)
31485     {
31486         //Roo.log('adjustForComponents ');
31487         if(this.resizeEl != this.el){
31488             width -= this.el.getFrameWidth('lr');
31489             height -= this.el.getFrameWidth('tb');
31490         }
31491         if(this.toolbar){
31492             var te = this.toolbar.getEl();
31493             height -= te.getHeight();
31494             te.setWidth(width);
31495         }
31496         if(this.footer){
31497             var te = this.footer.getEl();
31498             //Roo.log("footer:" + te.getHeight());
31499             
31500             height -= te.getHeight();
31501             te.setWidth(width);
31502         }
31503         
31504         
31505         if(this.adjustments){
31506             width += this.adjustments[0];
31507             height += this.adjustments[1];
31508         }
31509         return {"width": width, "height": height};
31510     },
31511     
31512     setSize : function(width, height){
31513         if(this.fitToFrame && !this.ignoreResize(width, height)){
31514             if(this.fitContainer && this.resizeEl != this.el){
31515                 this.el.setSize(width, height);
31516             }
31517             var size = this.adjustForComponents(width, height);
31518             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31519             this.fireEvent('resize', this, size.width, size.height);
31520         }
31521     },
31522     
31523     /**
31524      * Returns this panel's title
31525      * @return {String} 
31526      */
31527     getTitle : function(){
31528         return this.title;
31529     },
31530     
31531     /**
31532      * Set this panel's title
31533      * @param {String} title
31534      */
31535     setTitle : function(title){
31536         this.title = title;
31537         if(this.region){
31538             this.region.updatePanelTitle(this, title);
31539         }
31540     },
31541     
31542     /**
31543      * Returns true is this panel was configured to be closable
31544      * @return {Boolean} 
31545      */
31546     isClosable : function(){
31547         return this.closable;
31548     },
31549     
31550     beforeSlide : function(){
31551         this.el.clip();
31552         this.resizeEl.clip();
31553     },
31554     
31555     afterSlide : function(){
31556         this.el.unclip();
31557         this.resizeEl.unclip();
31558     },
31559     
31560     /**
31561      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31562      *   Will fail silently if the {@link #setUrl} method has not been called.
31563      *   This does not activate the panel, just updates its content.
31564      */
31565     refresh : function(){
31566         if(this.refreshDelegate){
31567            this.loaded = false;
31568            this.refreshDelegate();
31569         }
31570     },
31571     
31572     /**
31573      * Destroys this panel
31574      */
31575     destroy : function(){
31576         this.el.removeAllListeners();
31577         var tempEl = document.createElement("span");
31578         tempEl.appendChild(this.el.dom);
31579         tempEl.innerHTML = "";
31580         this.el.remove();
31581         this.el = null;
31582     },
31583     
31584     /**
31585      * form - if the content panel contains a form - this is a reference to it.
31586      * @type {Roo.form.Form}
31587      */
31588     form : false,
31589     /**
31590      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31591      *    This contains a reference to it.
31592      * @type {Roo.View}
31593      */
31594     view : false,
31595     
31596       /**
31597      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31598      * <pre><code>
31599
31600 layout.addxtype({
31601        xtype : 'Form',
31602        items: [ .... ]
31603    }
31604 );
31605
31606 </code></pre>
31607      * @param {Object} cfg Xtype definition of item to add.
31608      */
31609     
31610     addxtype : function(cfg) {
31611         // add form..
31612         if (cfg.xtype.match(/^Form$/)) {
31613             
31614             var el;
31615             //if (this.footer) {
31616             //    el = this.footer.container.insertSibling(false, 'before');
31617             //} else {
31618                 el = this.el.createChild();
31619             //}
31620
31621             this.form = new  Roo.form.Form(cfg);
31622             
31623             
31624             if ( this.form.allItems.length) {
31625                 this.form.render(el.dom);
31626             }
31627             return this.form;
31628         }
31629         // should only have one of theses..
31630         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31631             // views.. should not be just added - used named prop 'view''
31632             
31633             cfg.el = this.el.appendChild(document.createElement("div"));
31634             // factory?
31635             
31636             var ret = new Roo.factory(cfg);
31637              
31638              ret.render && ret.render(false, ''); // render blank..
31639             this.view = ret;
31640             return ret;
31641         }
31642         return false;
31643     }
31644 });
31645
31646 /**
31647  * @class Roo.GridPanel
31648  * @extends Roo.ContentPanel
31649  * @constructor
31650  * Create a new GridPanel.
31651  * @param {Roo.grid.Grid} grid The grid for this panel
31652  * @param {String/Object} config A string to set only the panel's title, or a config object
31653  */
31654 Roo.GridPanel = function(grid, config){
31655     
31656   
31657     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31658         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31659         
31660     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31661     
31662     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31663     
31664     if(this.toolbar){
31665         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31666     }
31667     // xtype created footer. - not sure if will work as we normally have to render first..
31668     if (this.footer && !this.footer.el && this.footer.xtype) {
31669         
31670         this.footer.container = this.grid.getView().getFooterPanel(true);
31671         this.footer.dataSource = this.grid.dataSource;
31672         this.footer = Roo.factory(this.footer, Roo);
31673         
31674     }
31675     
31676     grid.monitorWindowResize = false; // turn off autosizing
31677     grid.autoHeight = false;
31678     grid.autoWidth = false;
31679     this.grid = grid;
31680     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31681 };
31682
31683 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31684     getId : function(){
31685         return this.grid.id;
31686     },
31687     
31688     /**
31689      * Returns the grid for this panel
31690      * @return {Roo.grid.Grid} 
31691      */
31692     getGrid : function(){
31693         return this.grid;    
31694     },
31695     
31696     setSize : function(width, height){
31697         if(!this.ignoreResize(width, height)){
31698             var grid = this.grid;
31699             var size = this.adjustForComponents(width, height);
31700             grid.getGridEl().setSize(size.width, size.height);
31701             grid.autoSize();
31702         }
31703     },
31704     
31705     beforeSlide : function(){
31706         this.grid.getView().scroller.clip();
31707     },
31708     
31709     afterSlide : function(){
31710         this.grid.getView().scroller.unclip();
31711     },
31712     
31713     destroy : function(){
31714         this.grid.destroy();
31715         delete this.grid;
31716         Roo.GridPanel.superclass.destroy.call(this); 
31717     }
31718 });
31719
31720
31721 /**
31722  * @class Roo.NestedLayoutPanel
31723  * @extends Roo.ContentPanel
31724  * @constructor
31725  * Create a new NestedLayoutPanel.
31726  * 
31727  * 
31728  * @param {Roo.BorderLayout} layout The layout for this panel
31729  * @param {String/Object} config A string to set only the title or a config object
31730  */
31731 Roo.NestedLayoutPanel = function(layout, config)
31732 {
31733     // construct with only one argument..
31734     /* FIXME - implement nicer consturctors
31735     if (layout.layout) {
31736         config = layout;
31737         layout = config.layout;
31738         delete config.layout;
31739     }
31740     if (layout.xtype && !layout.getEl) {
31741         // then layout needs constructing..
31742         layout = Roo.factory(layout, Roo);
31743     }
31744     */
31745     
31746     
31747     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31748     
31749     layout.monitorWindowResize = false; // turn off autosizing
31750     this.layout = layout;
31751     this.layout.getEl().addClass("x-layout-nested-layout");
31752     
31753     
31754     
31755     
31756 };
31757
31758 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31759
31760     setSize : function(width, height){
31761         if(!this.ignoreResize(width, height)){
31762             var size = this.adjustForComponents(width, height);
31763             var el = this.layout.getEl();
31764             el.setSize(size.width, size.height);
31765             var touch = el.dom.offsetWidth;
31766             this.layout.layout();
31767             // ie requires a double layout on the first pass
31768             if(Roo.isIE && !this.initialized){
31769                 this.initialized = true;
31770                 this.layout.layout();
31771             }
31772         }
31773     },
31774     
31775     // activate all subpanels if not currently active..
31776     
31777     setActiveState : function(active){
31778         this.active = active;
31779         if(!active){
31780             this.fireEvent("deactivate", this);
31781             return;
31782         }
31783         
31784         this.fireEvent("activate", this);
31785         // not sure if this should happen before or after..
31786         if (!this.layout) {
31787             return; // should not happen..
31788         }
31789         var reg = false;
31790         for (var r in this.layout.regions) {
31791             reg = this.layout.getRegion(r);
31792             if (reg.getActivePanel()) {
31793                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31794                 reg.setActivePanel(reg.getActivePanel());
31795                 continue;
31796             }
31797             if (!reg.panels.length) {
31798                 continue;
31799             }
31800             reg.showPanel(reg.getPanel(0));
31801         }
31802         
31803         
31804         
31805         
31806     },
31807     
31808     /**
31809      * Returns the nested BorderLayout for this panel
31810      * @return {Roo.BorderLayout} 
31811      */
31812     getLayout : function(){
31813         return this.layout;
31814     },
31815     
31816      /**
31817      * Adds a xtype elements to the layout of the nested panel
31818      * <pre><code>
31819
31820 panel.addxtype({
31821        xtype : 'ContentPanel',
31822        region: 'west',
31823        items: [ .... ]
31824    }
31825 );
31826
31827 panel.addxtype({
31828         xtype : 'NestedLayoutPanel',
31829         region: 'west',
31830         layout: {
31831            center: { },
31832            west: { }   
31833         },
31834         items : [ ... list of content panels or nested layout panels.. ]
31835    }
31836 );
31837 </code></pre>
31838      * @param {Object} cfg Xtype definition of item to add.
31839      */
31840     addxtype : function(cfg) {
31841         return this.layout.addxtype(cfg);
31842     
31843     }
31844 });
31845
31846 Roo.ScrollPanel = function(el, config, content){
31847     config = config || {};
31848     config.fitToFrame = true;
31849     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31850     
31851     this.el.dom.style.overflow = "hidden";
31852     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31853     this.el.removeClass("x-layout-inactive-content");
31854     this.el.on("mousewheel", this.onWheel, this);
31855
31856     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31857     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31858     up.unselectable(); down.unselectable();
31859     up.on("click", this.scrollUp, this);
31860     down.on("click", this.scrollDown, this);
31861     up.addClassOnOver("x-scroller-btn-over");
31862     down.addClassOnOver("x-scroller-btn-over");
31863     up.addClassOnClick("x-scroller-btn-click");
31864     down.addClassOnClick("x-scroller-btn-click");
31865     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31866
31867     this.resizeEl = this.el;
31868     this.el = wrap; this.up = up; this.down = down;
31869 };
31870
31871 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31872     increment : 100,
31873     wheelIncrement : 5,
31874     scrollUp : function(){
31875         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31876     },
31877
31878     scrollDown : function(){
31879         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31880     },
31881
31882     afterScroll : function(){
31883         var el = this.resizeEl;
31884         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31885         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31886         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31887     },
31888
31889     setSize : function(){
31890         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31891         this.afterScroll();
31892     },
31893
31894     onWheel : function(e){
31895         var d = e.getWheelDelta();
31896         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31897         this.afterScroll();
31898         e.stopEvent();
31899     },
31900
31901     setContent : function(content, loadScripts){
31902         this.resizeEl.update(content, loadScripts);
31903     }
31904
31905 });
31906
31907
31908
31909 /**
31910  * @class Roo.TreePanel
31911  * @extends Roo.ContentPanel
31912  * Treepanel component
31913  * 
31914  * @constructor
31915  * Create a new TreePanel. - defaults to fit/scoll contents.
31916  * @param {String/Object} config A string to set only the panel's title, or a config object
31917  */
31918 Roo.TreePanel = function(config){
31919     var el = config.el;
31920     var tree = config.tree;
31921     delete config.tree; 
31922     delete config.el; // hopefull!
31923     
31924     // wrapper for IE7 strict & safari scroll issue
31925     
31926     var treeEl = el.createChild();
31927     config.resizeEl = treeEl;
31928     
31929     
31930     
31931     Roo.TreePanel.superclass.constructor.call(this, el, config);
31932  
31933  
31934     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31935     //console.log(tree);
31936     this.on('activate', function()
31937     {
31938         if (this.tree.rendered) {
31939             return;
31940         }
31941         //console.log('render tree');
31942         this.tree.render();
31943     });
31944     // this should not be needed.. - it's actually the 'el' that resizes?
31945     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31946     
31947     //this.on('resize',  function (cp, w, h) {
31948     //        this.tree.innerCt.setWidth(w);
31949     //        this.tree.innerCt.setHeight(h);
31950     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
31951     //});
31952
31953         
31954     
31955 };
31956
31957 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31958     fitToFrame : true,
31959     autoScroll : true,
31960     /*
31961      * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31962      */
31963     tree : false
31964
31965 });
31966
31967
31968
31969
31970
31971
31972
31973
31974
31975
31976
31977 /*
31978  * Based on:
31979  * Ext JS Library 1.1.1
31980  * Copyright(c) 2006-2007, Ext JS, LLC.
31981  *
31982  * Originally Released Under LGPL - original licence link has changed is not relivant.
31983  *
31984  * Fork - LGPL
31985  * <script type="text/javascript">
31986  */
31987  
31988
31989 /**
31990  * @class Roo.ReaderLayout
31991  * @extends Roo.BorderLayout
31992  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31993  * center region containing two nested regions (a top one for a list view and one for item preview below),
31994  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31995  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31996  * expedites the setup of the overall layout and regions for this common application style.
31997  * Example:
31998  <pre><code>
31999 var reader = new Roo.ReaderLayout();
32000 var CP = Roo.ContentPanel;  // shortcut for adding
32001
32002 reader.beginUpdate();
32003 reader.add("north", new CP("north", "North"));
32004 reader.add("west", new CP("west", {title: "West"}));
32005 reader.add("east", new CP("east", {title: "East"}));
32006
32007 reader.regions.listView.add(new CP("listView", "List"));
32008 reader.regions.preview.add(new CP("preview", "Preview"));
32009 reader.endUpdate();
32010 </code></pre>
32011 * @constructor
32012 * Create a new ReaderLayout
32013 * @param {Object} config Configuration options
32014 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32015 * document.body if omitted)
32016 */
32017 Roo.ReaderLayout = function(config, renderTo){
32018     var c = config || {size:{}};
32019     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32020         north: c.north !== false ? Roo.apply({
32021             split:false,
32022             initialSize: 32,
32023             titlebar: false
32024         }, c.north) : false,
32025         west: c.west !== false ? Roo.apply({
32026             split:true,
32027             initialSize: 200,
32028             minSize: 175,
32029             maxSize: 400,
32030             titlebar: true,
32031             collapsible: true,
32032             animate: true,
32033             margins:{left:5,right:0,bottom:5,top:5},
32034             cmargins:{left:5,right:5,bottom:5,top:5}
32035         }, c.west) : false,
32036         east: c.east !== false ? Roo.apply({
32037             split:true,
32038             initialSize: 200,
32039             minSize: 175,
32040             maxSize: 400,
32041             titlebar: true,
32042             collapsible: true,
32043             animate: true,
32044             margins:{left:0,right:5,bottom:5,top:5},
32045             cmargins:{left:5,right:5,bottom:5,top:5}
32046         }, c.east) : false,
32047         center: Roo.apply({
32048             tabPosition: 'top',
32049             autoScroll:false,
32050             closeOnTab: true,
32051             titlebar:false,
32052             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32053         }, c.center)
32054     });
32055
32056     this.el.addClass('x-reader');
32057
32058     this.beginUpdate();
32059
32060     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32061         south: c.preview !== false ? Roo.apply({
32062             split:true,
32063             initialSize: 200,
32064             minSize: 100,
32065             autoScroll:true,
32066             collapsible:true,
32067             titlebar: true,
32068             cmargins:{top:5,left:0, right:0, bottom:0}
32069         }, c.preview) : false,
32070         center: Roo.apply({
32071             autoScroll:false,
32072             titlebar:false,
32073             minHeight:200
32074         }, c.listView)
32075     });
32076     this.add('center', new Roo.NestedLayoutPanel(inner,
32077             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32078
32079     this.endUpdate();
32080
32081     this.regions.preview = inner.getRegion('south');
32082     this.regions.listView = inner.getRegion('center');
32083 };
32084
32085 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32086  * Based on:
32087  * Ext JS Library 1.1.1
32088  * Copyright(c) 2006-2007, Ext JS, LLC.
32089  *
32090  * Originally Released Under LGPL - original licence link has changed is not relivant.
32091  *
32092  * Fork - LGPL
32093  * <script type="text/javascript">
32094  */
32095  
32096 /**
32097  * @class Roo.grid.Grid
32098  * @extends Roo.util.Observable
32099  * This class represents the primary interface of a component based grid control.
32100  * <br><br>Usage:<pre><code>
32101  var grid = new Roo.grid.Grid("my-container-id", {
32102      ds: myDataStore,
32103      cm: myColModel,
32104      selModel: mySelectionModel,
32105      autoSizeColumns: true,
32106      monitorWindowResize: false,
32107      trackMouseOver: true
32108  });
32109  // set any options
32110  grid.render();
32111  * </code></pre>
32112  * <b>Common Problems:</b><br/>
32113  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32114  * element will correct this<br/>
32115  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32116  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32117  * are unpredictable.<br/>
32118  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32119  * grid to calculate dimensions/offsets.<br/>
32120   * @constructor
32121  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32122  * The container MUST have some type of size defined for the grid to fill. The container will be
32123  * automatically set to position relative if it isn't already.
32124  * @param {Object} config A config object that sets properties on this grid.
32125  */
32126 Roo.grid.Grid = function(container, config){
32127         // initialize the container
32128         this.container = Roo.get(container);
32129         this.container.update("");
32130         this.container.setStyle("overflow", "hidden");
32131     this.container.addClass('x-grid-container');
32132
32133     this.id = this.container.id;
32134
32135     Roo.apply(this, config);
32136     // check and correct shorthanded configs
32137     if(this.ds){
32138         this.dataSource = this.ds;
32139         delete this.ds;
32140     }
32141     if(this.cm){
32142         this.colModel = this.cm;
32143         delete this.cm;
32144     }
32145     if(this.sm){
32146         this.selModel = this.sm;
32147         delete this.sm;
32148     }
32149
32150     if (this.selModel) {
32151         this.selModel = Roo.factory(this.selModel, Roo.grid);
32152         this.sm = this.selModel;
32153         this.sm.xmodule = this.xmodule || false;
32154     }
32155     if (typeof(this.colModel.config) == 'undefined') {
32156         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32157         this.cm = this.colModel;
32158         this.cm.xmodule = this.xmodule || false;
32159     }
32160     if (this.dataSource) {
32161         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32162         this.ds = this.dataSource;
32163         this.ds.xmodule = this.xmodule || false;
32164          
32165     }
32166     
32167     
32168     
32169     if(this.width){
32170         this.container.setWidth(this.width);
32171     }
32172
32173     if(this.height){
32174         this.container.setHeight(this.height);
32175     }
32176     /** @private */
32177         this.addEvents({
32178         // raw events
32179         /**
32180          * @event click
32181          * The raw click event for the entire grid.
32182          * @param {Roo.EventObject} e
32183          */
32184         "click" : true,
32185         /**
32186          * @event dblclick
32187          * The raw dblclick event for the entire grid.
32188          * @param {Roo.EventObject} e
32189          */
32190         "dblclick" : true,
32191         /**
32192          * @event contextmenu
32193          * The raw contextmenu event for the entire grid.
32194          * @param {Roo.EventObject} e
32195          */
32196         "contextmenu" : true,
32197         /**
32198          * @event mousedown
32199          * The raw mousedown event for the entire grid.
32200          * @param {Roo.EventObject} e
32201          */
32202         "mousedown" : true,
32203         /**
32204          * @event mouseup
32205          * The raw mouseup event for the entire grid.
32206          * @param {Roo.EventObject} e
32207          */
32208         "mouseup" : true,
32209         /**
32210          * @event mouseover
32211          * The raw mouseover event for the entire grid.
32212          * @param {Roo.EventObject} e
32213          */
32214         "mouseover" : true,
32215         /**
32216          * @event mouseout
32217          * The raw mouseout event for the entire grid.
32218          * @param {Roo.EventObject} e
32219          */
32220         "mouseout" : true,
32221         /**
32222          * @event keypress
32223          * The raw keypress event for the entire grid.
32224          * @param {Roo.EventObject} e
32225          */
32226         "keypress" : true,
32227         /**
32228          * @event keydown
32229          * The raw keydown event for the entire grid.
32230          * @param {Roo.EventObject} e
32231          */
32232         "keydown" : true,
32233
32234         // custom events
32235
32236         /**
32237          * @event cellclick
32238          * Fires when a cell is clicked
32239          * @param {Grid} this
32240          * @param {Number} rowIndex
32241          * @param {Number} columnIndex
32242          * @param {Roo.EventObject} e
32243          */
32244         "cellclick" : true,
32245         /**
32246          * @event celldblclick
32247          * Fires when a cell is double clicked
32248          * @param {Grid} this
32249          * @param {Number} rowIndex
32250          * @param {Number} columnIndex
32251          * @param {Roo.EventObject} e
32252          */
32253         "celldblclick" : true,
32254         /**
32255          * @event rowclick
32256          * Fires when a row is clicked
32257          * @param {Grid} this
32258          * @param {Number} rowIndex
32259          * @param {Roo.EventObject} e
32260          */
32261         "rowclick" : true,
32262         /**
32263          * @event rowdblclick
32264          * Fires when a row is double clicked
32265          * @param {Grid} this
32266          * @param {Number} rowIndex
32267          * @param {Roo.EventObject} e
32268          */
32269         "rowdblclick" : true,
32270         /**
32271          * @event headerclick
32272          * Fires when a header is clicked
32273          * @param {Grid} this
32274          * @param {Number} columnIndex
32275          * @param {Roo.EventObject} e
32276          */
32277         "headerclick" : true,
32278         /**
32279          * @event headerdblclick
32280          * Fires when a header cell is double clicked
32281          * @param {Grid} this
32282          * @param {Number} columnIndex
32283          * @param {Roo.EventObject} e
32284          */
32285         "headerdblclick" : true,
32286         /**
32287          * @event rowcontextmenu
32288          * Fires when a row is right clicked
32289          * @param {Grid} this
32290          * @param {Number} rowIndex
32291          * @param {Roo.EventObject} e
32292          */
32293         "rowcontextmenu" : true,
32294         /**
32295          * @event cellcontextmenu
32296          * Fires when a cell is right clicked
32297          * @param {Grid} this
32298          * @param {Number} rowIndex
32299          * @param {Number} cellIndex
32300          * @param {Roo.EventObject} e
32301          */
32302          "cellcontextmenu" : true,
32303         /**
32304          * @event headercontextmenu
32305          * Fires when a header is right clicked
32306          * @param {Grid} this
32307          * @param {Number} columnIndex
32308          * @param {Roo.EventObject} e
32309          */
32310         "headercontextmenu" : true,
32311         /**
32312          * @event bodyscroll
32313          * Fires when the body element is scrolled
32314          * @param {Number} scrollLeft
32315          * @param {Number} scrollTop
32316          */
32317         "bodyscroll" : true,
32318         /**
32319          * @event columnresize
32320          * Fires when the user resizes a column
32321          * @param {Number} columnIndex
32322          * @param {Number} newSize
32323          */
32324         "columnresize" : true,
32325         /**
32326          * @event columnmove
32327          * Fires when the user moves a column
32328          * @param {Number} oldIndex
32329          * @param {Number} newIndex
32330          */
32331         "columnmove" : true,
32332         /**
32333          * @event startdrag
32334          * Fires when row(s) start being dragged
32335          * @param {Grid} this
32336          * @param {Roo.GridDD} dd The drag drop object
32337          * @param {event} e The raw browser event
32338          */
32339         "startdrag" : true,
32340         /**
32341          * @event enddrag
32342          * Fires when a drag operation is complete
32343          * @param {Grid} this
32344          * @param {Roo.GridDD} dd The drag drop object
32345          * @param {event} e The raw browser event
32346          */
32347         "enddrag" : true,
32348         /**
32349          * @event dragdrop
32350          * Fires when dragged row(s) are dropped on a valid DD target
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         "dragdrop" : true,
32357         /**
32358          * @event dragover
32359          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32360          * @param {Grid} this
32361          * @param {Roo.GridDD} dd The drag drop object
32362          * @param {String} targetId The target drag drop object
32363          * @param {event} e The raw browser event
32364          */
32365         "dragover" : true,
32366         /**
32367          * @event dragenter
32368          *  Fires when the dragged row(s) first cross another DD target while being dragged
32369          * @param {Grid} this
32370          * @param {Roo.GridDD} dd The drag drop object
32371          * @param {String} targetId The target drag drop object
32372          * @param {event} e The raw browser event
32373          */
32374         "dragenter" : true,
32375         /**
32376          * @event dragout
32377          * Fires when the dragged row(s) leave another DD target while being dragged
32378          * @param {Grid} this
32379          * @param {Roo.GridDD} dd The drag drop object
32380          * @param {String} targetId The target drag drop object
32381          * @param {event} e The raw browser event
32382          */
32383         "dragout" : true,
32384         /**
32385          * @event rowclass
32386          * Fires when a row is rendered, so you can change add a style to it.
32387          * @param {GridView} gridview   The grid view
32388          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32389          */
32390         'rowclass' : true,
32391
32392         /**
32393          * @event render
32394          * Fires when the grid is rendered
32395          * @param {Grid} grid
32396          */
32397         'render' : true
32398     });
32399
32400     Roo.grid.Grid.superclass.constructor.call(this);
32401 };
32402 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32403     
32404     /**
32405      * @cfg {String} ddGroup - drag drop group.
32406      */
32407       /**
32408      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
32409      */
32410
32411     /**
32412      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32413      */
32414     minColumnWidth : 25,
32415
32416     /**
32417      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32418      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32419      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32420      */
32421     autoSizeColumns : false,
32422
32423     /**
32424      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32425      */
32426     autoSizeHeaders : true,
32427
32428     /**
32429      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32430      */
32431     monitorWindowResize : true,
32432
32433     /**
32434      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32435      * rows measured to get a columns size. Default is 0 (all rows).
32436      */
32437     maxRowsToMeasure : 0,
32438
32439     /**
32440      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32441      */
32442     trackMouseOver : true,
32443
32444     /**
32445     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32446     */
32447       /**
32448     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
32449     */
32450     
32451     /**
32452     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32453     */
32454     enableDragDrop : false,
32455     
32456     /**
32457     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32458     */
32459     enableColumnMove : true,
32460     
32461     /**
32462     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32463     */
32464     enableColumnHide : true,
32465     
32466     /**
32467     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32468     */
32469     enableRowHeightSync : false,
32470     
32471     /**
32472     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32473     */
32474     stripeRows : true,
32475     
32476     /**
32477     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32478     */
32479     autoHeight : false,
32480
32481     /**
32482      * @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.
32483      */
32484     autoExpandColumn : false,
32485
32486     /**
32487     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32488     * Default is 50.
32489     */
32490     autoExpandMin : 50,
32491
32492     /**
32493     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32494     */
32495     autoExpandMax : 1000,
32496
32497     /**
32498     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32499     */
32500     view : null,
32501
32502     /**
32503     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32504     */
32505     loadMask : false,
32506     /**
32507     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32508     */
32509     dropTarget: false,
32510     
32511    
32512     
32513     // private
32514     rendered : false,
32515
32516     /**
32517     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32518     * of a fixed width. Default is false.
32519     */
32520     /**
32521     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32522     */
32523     
32524     
32525     /**
32526     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32527     * %0 is replaced with the number of selected rows.
32528     */
32529     ddText : "{0} selected row{1}",
32530     
32531     
32532     /**
32533      * Called once after all setup has been completed and the grid is ready to be rendered.
32534      * @return {Roo.grid.Grid} this
32535      */
32536     render : function()
32537     {
32538         var c = this.container;
32539         // try to detect autoHeight/width mode
32540         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32541             this.autoHeight = true;
32542         }
32543         var view = this.getView();
32544         view.init(this);
32545
32546         c.on("click", this.onClick, this);
32547         c.on("dblclick", this.onDblClick, this);
32548         c.on("contextmenu", this.onContextMenu, this);
32549         c.on("keydown", this.onKeyDown, this);
32550         if (Roo.isTouch) {
32551             c.on("touchstart", this.onTouchStart, this);
32552         }
32553
32554         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32555
32556         this.getSelectionModel().init(this);
32557
32558         view.render();
32559
32560         if(this.loadMask){
32561             this.loadMask = new Roo.LoadMask(this.container,
32562                     Roo.apply({store:this.dataSource}, this.loadMask));
32563         }
32564         
32565         
32566         if (this.toolbar && this.toolbar.xtype) {
32567             this.toolbar.container = this.getView().getHeaderPanel(true);
32568             this.toolbar = new Roo.Toolbar(this.toolbar);
32569         }
32570         if (this.footer && this.footer.xtype) {
32571             this.footer.dataSource = this.getDataSource();
32572             this.footer.container = this.getView().getFooterPanel(true);
32573             this.footer = Roo.factory(this.footer, Roo);
32574         }
32575         if (this.dropTarget && this.dropTarget.xtype) {
32576             delete this.dropTarget.xtype;
32577             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32578         }
32579         
32580         
32581         this.rendered = true;
32582         this.fireEvent('render', this);
32583         return this;
32584     },
32585
32586     /**
32587      * Reconfigures the grid to use a different Store and Column Model.
32588      * The View will be bound to the new objects and refreshed.
32589      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32590      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32591      */
32592     reconfigure : function(dataSource, colModel){
32593         if(this.loadMask){
32594             this.loadMask.destroy();
32595             this.loadMask = new Roo.LoadMask(this.container,
32596                     Roo.apply({store:dataSource}, this.loadMask));
32597         }
32598         this.view.bind(dataSource, colModel);
32599         this.dataSource = dataSource;
32600         this.colModel = colModel;
32601         this.view.refresh(true);
32602     },
32603     /**
32604      * addColumns
32605      * Add's a column, default at the end..
32606      
32607      * @param {int} position to add (default end)
32608      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
32609      */
32610     addColumns : function(pos, ar)
32611     {
32612         
32613         for (var i =0;i< ar.length;i++) {
32614             var cfg = ar[i];
32615             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
32616             this.cm.lookup[cfg.id] = cfg;
32617         }
32618         
32619         
32620         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
32621             pos = this.cm.config.length; //this.cm.config.push(cfg);
32622         } 
32623         pos = Math.max(0,pos);
32624         ar.unshift(0);
32625         ar.unshift(pos);
32626         this.cm.config.splice.apply(this.cm.config, ar);
32627         
32628         
32629         
32630         this.view.generateRules(this.cm);
32631         this.view.refresh(true);
32632         
32633     },
32634     
32635     
32636     
32637     
32638     // private
32639     onKeyDown : function(e){
32640         this.fireEvent("keydown", e);
32641     },
32642
32643     /**
32644      * Destroy this grid.
32645      * @param {Boolean} removeEl True to remove the element
32646      */
32647     destroy : function(removeEl, keepListeners){
32648         if(this.loadMask){
32649             this.loadMask.destroy();
32650         }
32651         var c = this.container;
32652         c.removeAllListeners();
32653         this.view.destroy();
32654         this.colModel.purgeListeners();
32655         if(!keepListeners){
32656             this.purgeListeners();
32657         }
32658         c.update("");
32659         if(removeEl === true){
32660             c.remove();
32661         }
32662     },
32663
32664     // private
32665     processEvent : function(name, e){
32666         // does this fire select???
32667         //Roo.log('grid:processEvent '  + name);
32668         
32669         if (name != 'touchstart' ) {
32670             this.fireEvent(name, e);    
32671         }
32672         
32673         var t = e.getTarget();
32674         var v = this.view;
32675         var header = v.findHeaderIndex(t);
32676         if(header !== false){
32677             var ename = name == 'touchstart' ? 'click' : name;
32678              
32679             this.fireEvent("header" + ename, this, header, e);
32680         }else{
32681             var row = v.findRowIndex(t);
32682             var cell = v.findCellIndex(t);
32683             if (name == 'touchstart') {
32684                 // first touch is always a click.
32685                 // hopefull this happens after selection is updated.?
32686                 name = false;
32687                 
32688                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32689                     var cs = this.selModel.getSelectedCell();
32690                     if (row == cs[0] && cell == cs[1]){
32691                         name = 'dblclick';
32692                     }
32693                 }
32694                 if (typeof(this.selModel.getSelections) != 'undefined') {
32695                     var cs = this.selModel.getSelections();
32696                     var ds = this.dataSource;
32697                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32698                         name = 'dblclick';
32699                     }
32700                 }
32701                 if (!name) {
32702                     return;
32703                 }
32704             }
32705             
32706             
32707             if(row !== false){
32708                 this.fireEvent("row" + name, this, row, e);
32709                 if(cell !== false){
32710                     this.fireEvent("cell" + name, this, row, cell, e);
32711                 }
32712             }
32713         }
32714     },
32715
32716     // private
32717     onClick : function(e){
32718         this.processEvent("click", e);
32719     },
32720    // private
32721     onTouchStart : function(e){
32722         this.processEvent("touchstart", e);
32723     },
32724
32725     // private
32726     onContextMenu : function(e, t){
32727         this.processEvent("contextmenu", e);
32728     },
32729
32730     // private
32731     onDblClick : function(e){
32732         this.processEvent("dblclick", e);
32733     },
32734
32735     // private
32736     walkCells : function(row, col, step, fn, scope){
32737         var cm = this.colModel, clen = cm.getColumnCount();
32738         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32739         if(step < 0){
32740             if(col < 0){
32741                 row--;
32742                 first = false;
32743             }
32744             while(row >= 0){
32745                 if(!first){
32746                     col = clen-1;
32747                 }
32748                 first = false;
32749                 while(col >= 0){
32750                     if(fn.call(scope || this, row, col, cm) === true){
32751                         return [row, col];
32752                     }
32753                     col--;
32754                 }
32755                 row--;
32756             }
32757         } else {
32758             if(col >= clen){
32759                 row++;
32760                 first = false;
32761             }
32762             while(row < rlen){
32763                 if(!first){
32764                     col = 0;
32765                 }
32766                 first = false;
32767                 while(col < clen){
32768                     if(fn.call(scope || this, row, col, cm) === true){
32769                         return [row, col];
32770                     }
32771                     col++;
32772                 }
32773                 row++;
32774             }
32775         }
32776         return null;
32777     },
32778
32779     // private
32780     getSelections : function(){
32781         return this.selModel.getSelections();
32782     },
32783
32784     /**
32785      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32786      * but if manual update is required this method will initiate it.
32787      */
32788     autoSize : function(){
32789         if(this.rendered){
32790             this.view.layout();
32791             if(this.view.adjustForScroll){
32792                 this.view.adjustForScroll();
32793             }
32794         }
32795     },
32796
32797     /**
32798      * Returns the grid's underlying element.
32799      * @return {Element} The element
32800      */
32801     getGridEl : function(){
32802         return this.container;
32803     },
32804
32805     // private for compatibility, overridden by editor grid
32806     stopEditing : function(){},
32807
32808     /**
32809      * Returns the grid's SelectionModel.
32810      * @return {SelectionModel}
32811      */
32812     getSelectionModel : function(){
32813         if(!this.selModel){
32814             this.selModel = new Roo.grid.RowSelectionModel();
32815         }
32816         return this.selModel;
32817     },
32818
32819     /**
32820      * Returns the grid's DataSource.
32821      * @return {DataSource}
32822      */
32823     getDataSource : function(){
32824         return this.dataSource;
32825     },
32826
32827     /**
32828      * Returns the grid's ColumnModel.
32829      * @return {ColumnModel}
32830      */
32831     getColumnModel : function(){
32832         return this.colModel;
32833     },
32834
32835     /**
32836      * Returns the grid's GridView object.
32837      * @return {GridView}
32838      */
32839     getView : function(){
32840         if(!this.view){
32841             this.view = new Roo.grid.GridView(this.viewConfig);
32842             this.relayEvents(this.view, [
32843                 "beforerowremoved", "beforerowsinserted",
32844                 "beforerefresh", "rowremoved",
32845                 "rowsinserted", "rowupdated" ,"refresh"
32846             ]);
32847         }
32848         return this.view;
32849     },
32850     /**
32851      * Called to get grid's drag proxy text, by default returns this.ddText.
32852      * Override this to put something different in the dragged text.
32853      * @return {String}
32854      */
32855     getDragDropText : function(){
32856         var count = this.selModel.getCount();
32857         return String.format(this.ddText, count, count == 1 ? '' : 's');
32858     }
32859 });
32860 /*
32861  * Based on:
32862  * Ext JS Library 1.1.1
32863  * Copyright(c) 2006-2007, Ext JS, LLC.
32864  *
32865  * Originally Released Under LGPL - original licence link has changed is not relivant.
32866  *
32867  * Fork - LGPL
32868  * <script type="text/javascript">
32869  */
32870  
32871 Roo.grid.AbstractGridView = function(){
32872         this.grid = null;
32873         
32874         this.events = {
32875             "beforerowremoved" : true,
32876             "beforerowsinserted" : true,
32877             "beforerefresh" : true,
32878             "rowremoved" : true,
32879             "rowsinserted" : true,
32880             "rowupdated" : true,
32881             "refresh" : true
32882         };
32883     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32884 };
32885
32886 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32887     rowClass : "x-grid-row",
32888     cellClass : "x-grid-cell",
32889     tdClass : "x-grid-td",
32890     hdClass : "x-grid-hd",
32891     splitClass : "x-grid-hd-split",
32892     
32893     init: function(grid){
32894         this.grid = grid;
32895                 var cid = this.grid.getGridEl().id;
32896         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32897         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32898         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32899         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32900         },
32901         
32902     getColumnRenderers : function(){
32903         var renderers = [];
32904         var cm = this.grid.colModel;
32905         var colCount = cm.getColumnCount();
32906         for(var i = 0; i < colCount; i++){
32907             renderers[i] = cm.getRenderer(i);
32908         }
32909         return renderers;
32910     },
32911     
32912     getColumnIds : function(){
32913         var ids = [];
32914         var cm = this.grid.colModel;
32915         var colCount = cm.getColumnCount();
32916         for(var i = 0; i < colCount; i++){
32917             ids[i] = cm.getColumnId(i);
32918         }
32919         return ids;
32920     },
32921     
32922     getDataIndexes : function(){
32923         if(!this.indexMap){
32924             this.indexMap = this.buildIndexMap();
32925         }
32926         return this.indexMap.colToData;
32927     },
32928     
32929     getColumnIndexByDataIndex : function(dataIndex){
32930         if(!this.indexMap){
32931             this.indexMap = this.buildIndexMap();
32932         }
32933         return this.indexMap.dataToCol[dataIndex];
32934     },
32935     
32936     /**
32937      * Set a css style for a column dynamically. 
32938      * @param {Number} colIndex The index of the column
32939      * @param {String} name The css property name
32940      * @param {String} value The css value
32941      */
32942     setCSSStyle : function(colIndex, name, value){
32943         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32944         Roo.util.CSS.updateRule(selector, name, value);
32945     },
32946     
32947     generateRules : function(cm){
32948         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32949         Roo.util.CSS.removeStyleSheet(rulesId);
32950         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32951             var cid = cm.getColumnId(i);
32952             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32953                          this.tdSelector, cid, " {\n}\n",
32954                          this.hdSelector, cid, " {\n}\n",
32955                          this.splitSelector, cid, " {\n}\n");
32956         }
32957         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32958     }
32959 });/*
32960  * Based on:
32961  * Ext JS Library 1.1.1
32962  * Copyright(c) 2006-2007, Ext JS, LLC.
32963  *
32964  * Originally Released Under LGPL - original licence link has changed is not relivant.
32965  *
32966  * Fork - LGPL
32967  * <script type="text/javascript">
32968  */
32969
32970 // private
32971 // This is a support class used internally by the Grid components
32972 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32973     this.grid = grid;
32974     this.view = grid.getView();
32975     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32976     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32977     if(hd2){
32978         this.setHandleElId(Roo.id(hd));
32979         this.setOuterHandleElId(Roo.id(hd2));
32980     }
32981     this.scroll = false;
32982 };
32983 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32984     maxDragWidth: 120,
32985     getDragData : function(e){
32986         var t = Roo.lib.Event.getTarget(e);
32987         var h = this.view.findHeaderCell(t);
32988         if(h){
32989             return {ddel: h.firstChild, header:h};
32990         }
32991         return false;
32992     },
32993
32994     onInitDrag : function(e){
32995         this.view.headersDisabled = true;
32996         var clone = this.dragData.ddel.cloneNode(true);
32997         clone.id = Roo.id();
32998         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32999         this.proxy.update(clone);
33000         return true;
33001     },
33002
33003     afterValidDrop : function(){
33004         var v = this.view;
33005         setTimeout(function(){
33006             v.headersDisabled = false;
33007         }, 50);
33008     },
33009
33010     afterInvalidDrop : function(){
33011         var v = this.view;
33012         setTimeout(function(){
33013             v.headersDisabled = false;
33014         }, 50);
33015     }
33016 });
33017 /*
33018  * Based on:
33019  * Ext JS Library 1.1.1
33020  * Copyright(c) 2006-2007, Ext JS, LLC.
33021  *
33022  * Originally Released Under LGPL - original licence link has changed is not relivant.
33023  *
33024  * Fork - LGPL
33025  * <script type="text/javascript">
33026  */
33027 // private
33028 // This is a support class used internally by the Grid components
33029 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33030     this.grid = grid;
33031     this.view = grid.getView();
33032     // split the proxies so they don't interfere with mouse events
33033     this.proxyTop = Roo.DomHelper.append(document.body, {
33034         cls:"col-move-top", html:"&#160;"
33035     }, true);
33036     this.proxyBottom = Roo.DomHelper.append(document.body, {
33037         cls:"col-move-bottom", html:"&#160;"
33038     }, true);
33039     this.proxyTop.hide = this.proxyBottom.hide = function(){
33040         this.setLeftTop(-100,-100);
33041         this.setStyle("visibility", "hidden");
33042     };
33043     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33044     // temporarily disabled
33045     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33046     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33047 };
33048 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33049     proxyOffsets : [-4, -9],
33050     fly: Roo.Element.fly,
33051
33052     getTargetFromEvent : function(e){
33053         var t = Roo.lib.Event.getTarget(e);
33054         var cindex = this.view.findCellIndex(t);
33055         if(cindex !== false){
33056             return this.view.getHeaderCell(cindex);
33057         }
33058         return null;
33059     },
33060
33061     nextVisible : function(h){
33062         var v = this.view, cm = this.grid.colModel;
33063         h = h.nextSibling;
33064         while(h){
33065             if(!cm.isHidden(v.getCellIndex(h))){
33066                 return h;
33067             }
33068             h = h.nextSibling;
33069         }
33070         return null;
33071     },
33072
33073     prevVisible : function(h){
33074         var v = this.view, cm = this.grid.colModel;
33075         h = h.prevSibling;
33076         while(h){
33077             if(!cm.isHidden(v.getCellIndex(h))){
33078                 return h;
33079             }
33080             h = h.prevSibling;
33081         }
33082         return null;
33083     },
33084
33085     positionIndicator : function(h, n, e){
33086         var x = Roo.lib.Event.getPageX(e);
33087         var r = Roo.lib.Dom.getRegion(n.firstChild);
33088         var px, pt, py = r.top + this.proxyOffsets[1];
33089         if((r.right - x) <= (r.right-r.left)/2){
33090             px = r.right+this.view.borderWidth;
33091             pt = "after";
33092         }else{
33093             px = r.left;
33094             pt = "before";
33095         }
33096         var oldIndex = this.view.getCellIndex(h);
33097         var newIndex = this.view.getCellIndex(n);
33098
33099         if(this.grid.colModel.isFixed(newIndex)){
33100             return false;
33101         }
33102
33103         var locked = this.grid.colModel.isLocked(newIndex);
33104
33105         if(pt == "after"){
33106             newIndex++;
33107         }
33108         if(oldIndex < newIndex){
33109             newIndex--;
33110         }
33111         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33112             return false;
33113         }
33114         px +=  this.proxyOffsets[0];
33115         this.proxyTop.setLeftTop(px, py);
33116         this.proxyTop.show();
33117         if(!this.bottomOffset){
33118             this.bottomOffset = this.view.mainHd.getHeight();
33119         }
33120         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33121         this.proxyBottom.show();
33122         return pt;
33123     },
33124
33125     onNodeEnter : function(n, dd, e, data){
33126         if(data.header != n){
33127             this.positionIndicator(data.header, n, e);
33128         }
33129     },
33130
33131     onNodeOver : function(n, dd, e, data){
33132         var result = false;
33133         if(data.header != n){
33134             result = this.positionIndicator(data.header, n, e);
33135         }
33136         if(!result){
33137             this.proxyTop.hide();
33138             this.proxyBottom.hide();
33139         }
33140         return result ? this.dropAllowed : this.dropNotAllowed;
33141     },
33142
33143     onNodeOut : function(n, dd, e, data){
33144         this.proxyTop.hide();
33145         this.proxyBottom.hide();
33146     },
33147
33148     onNodeDrop : function(n, dd, e, data){
33149         var h = data.header;
33150         if(h != n){
33151             var cm = this.grid.colModel;
33152             var x = Roo.lib.Event.getPageX(e);
33153             var r = Roo.lib.Dom.getRegion(n.firstChild);
33154             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33155             var oldIndex = this.view.getCellIndex(h);
33156             var newIndex = this.view.getCellIndex(n);
33157             var locked = cm.isLocked(newIndex);
33158             if(pt == "after"){
33159                 newIndex++;
33160             }
33161             if(oldIndex < newIndex){
33162                 newIndex--;
33163             }
33164             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33165                 return false;
33166             }
33167             cm.setLocked(oldIndex, locked, true);
33168             cm.moveColumn(oldIndex, newIndex);
33169             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33170             return true;
33171         }
33172         return false;
33173     }
33174 });
33175 /*
33176  * Based on:
33177  * Ext JS Library 1.1.1
33178  * Copyright(c) 2006-2007, Ext JS, LLC.
33179  *
33180  * Originally Released Under LGPL - original licence link has changed is not relivant.
33181  *
33182  * Fork - LGPL
33183  * <script type="text/javascript">
33184  */
33185   
33186 /**
33187  * @class Roo.grid.GridView
33188  * @extends Roo.util.Observable
33189  *
33190  * @constructor
33191  * @param {Object} config
33192  */
33193 Roo.grid.GridView = function(config){
33194     Roo.grid.GridView.superclass.constructor.call(this);
33195     this.el = null;
33196
33197     Roo.apply(this, config);
33198 };
33199
33200 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33201
33202     unselectable :  'unselectable="on"',
33203     unselectableCls :  'x-unselectable',
33204     
33205     
33206     rowClass : "x-grid-row",
33207
33208     cellClass : "x-grid-col",
33209
33210     tdClass : "x-grid-td",
33211
33212     hdClass : "x-grid-hd",
33213
33214     splitClass : "x-grid-split",
33215
33216     sortClasses : ["sort-asc", "sort-desc"],
33217
33218     enableMoveAnim : false,
33219
33220     hlColor: "C3DAF9",
33221
33222     dh : Roo.DomHelper,
33223
33224     fly : Roo.Element.fly,
33225
33226     css : Roo.util.CSS,
33227
33228     borderWidth: 1,
33229
33230     splitOffset: 3,
33231
33232     scrollIncrement : 22,
33233
33234     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33235
33236     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33237
33238     bind : function(ds, cm){
33239         if(this.ds){
33240             this.ds.un("load", this.onLoad, this);
33241             this.ds.un("datachanged", this.onDataChange, this);
33242             this.ds.un("add", this.onAdd, this);
33243             this.ds.un("remove", this.onRemove, this);
33244             this.ds.un("update", this.onUpdate, this);
33245             this.ds.un("clear", this.onClear, this);
33246         }
33247         if(ds){
33248             ds.on("load", this.onLoad, this);
33249             ds.on("datachanged", this.onDataChange, this);
33250             ds.on("add", this.onAdd, this);
33251             ds.on("remove", this.onRemove, this);
33252             ds.on("update", this.onUpdate, this);
33253             ds.on("clear", this.onClear, this);
33254         }
33255         this.ds = ds;
33256
33257         if(this.cm){
33258             this.cm.un("widthchange", this.onColWidthChange, this);
33259             this.cm.un("headerchange", this.onHeaderChange, this);
33260             this.cm.un("hiddenchange", this.onHiddenChange, this);
33261             this.cm.un("columnmoved", this.onColumnMove, this);
33262             this.cm.un("columnlockchange", this.onColumnLock, this);
33263         }
33264         if(cm){
33265             this.generateRules(cm);
33266             cm.on("widthchange", this.onColWidthChange, this);
33267             cm.on("headerchange", this.onHeaderChange, this);
33268             cm.on("hiddenchange", this.onHiddenChange, this);
33269             cm.on("columnmoved", this.onColumnMove, this);
33270             cm.on("columnlockchange", this.onColumnLock, this);
33271         }
33272         this.cm = cm;
33273     },
33274
33275     init: function(grid){
33276         Roo.grid.GridView.superclass.init.call(this, grid);
33277
33278         this.bind(grid.dataSource, grid.colModel);
33279
33280         grid.on("headerclick", this.handleHeaderClick, this);
33281
33282         if(grid.trackMouseOver){
33283             grid.on("mouseover", this.onRowOver, this);
33284             grid.on("mouseout", this.onRowOut, this);
33285         }
33286         grid.cancelTextSelection = function(){};
33287         this.gridId = grid.id;
33288
33289         var tpls = this.templates || {};
33290
33291         if(!tpls.master){
33292             tpls.master = new Roo.Template(
33293                '<div class="x-grid" hidefocus="true">',
33294                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33295                   '<div class="x-grid-topbar"></div>',
33296                   '<div class="x-grid-scroller"><div></div></div>',
33297                   '<div class="x-grid-locked">',
33298                       '<div class="x-grid-header">{lockedHeader}</div>',
33299                       '<div class="x-grid-body">{lockedBody}</div>',
33300                   "</div>",
33301                   '<div class="x-grid-viewport">',
33302                       '<div class="x-grid-header">{header}</div>',
33303                       '<div class="x-grid-body">{body}</div>',
33304                   "</div>",
33305                   '<div class="x-grid-bottombar"></div>',
33306                  
33307                   '<div class="x-grid-resize-proxy">&#160;</div>',
33308                "</div>"
33309             );
33310             tpls.master.disableformats = true;
33311         }
33312
33313         if(!tpls.header){
33314             tpls.header = new Roo.Template(
33315                '<table border="0" cellspacing="0" cellpadding="0">',
33316                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33317                "</table>{splits}"
33318             );
33319             tpls.header.disableformats = true;
33320         }
33321         tpls.header.compile();
33322
33323         if(!tpls.hcell){
33324             tpls.hcell = new Roo.Template(
33325                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33326                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33327                 "</div></td>"
33328              );
33329              tpls.hcell.disableFormats = true;
33330         }
33331         tpls.hcell.compile();
33332
33333         if(!tpls.hsplit){
33334             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33335                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33336             tpls.hsplit.disableFormats = true;
33337         }
33338         tpls.hsplit.compile();
33339
33340         if(!tpls.body){
33341             tpls.body = new Roo.Template(
33342                '<table border="0" cellspacing="0" cellpadding="0">',
33343                "<tbody>{rows}</tbody>",
33344                "</table>"
33345             );
33346             tpls.body.disableFormats = true;
33347         }
33348         tpls.body.compile();
33349
33350         if(!tpls.row){
33351             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33352             tpls.row.disableFormats = true;
33353         }
33354         tpls.row.compile();
33355
33356         if(!tpls.cell){
33357             tpls.cell = new Roo.Template(
33358                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33359                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33360                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33361                 "</td>"
33362             );
33363             tpls.cell.disableFormats = true;
33364         }
33365         tpls.cell.compile();
33366
33367         this.templates = tpls;
33368     },
33369
33370     // remap these for backwards compat
33371     onColWidthChange : function(){
33372         this.updateColumns.apply(this, arguments);
33373     },
33374     onHeaderChange : function(){
33375         this.updateHeaders.apply(this, arguments);
33376     }, 
33377     onHiddenChange : function(){
33378         this.handleHiddenChange.apply(this, arguments);
33379     },
33380     onColumnMove : function(){
33381         this.handleColumnMove.apply(this, arguments);
33382     },
33383     onColumnLock : function(){
33384         this.handleLockChange.apply(this, arguments);
33385     },
33386
33387     onDataChange : function(){
33388         this.refresh();
33389         this.updateHeaderSortState();
33390     },
33391
33392     onClear : function(){
33393         this.refresh();
33394     },
33395
33396     onUpdate : function(ds, record){
33397         this.refreshRow(record);
33398     },
33399
33400     refreshRow : function(record){
33401         var ds = this.ds, index;
33402         if(typeof record == 'number'){
33403             index = record;
33404             record = ds.getAt(index);
33405         }else{
33406             index = ds.indexOf(record);
33407         }
33408         this.insertRows(ds, index, index, true);
33409         this.onRemove(ds, record, index+1, true);
33410         this.syncRowHeights(index, index);
33411         this.layout();
33412         this.fireEvent("rowupdated", this, index, record);
33413     },
33414
33415     onAdd : function(ds, records, index){
33416         this.insertRows(ds, index, index + (records.length-1));
33417     },
33418
33419     onRemove : function(ds, record, index, isUpdate){
33420         if(isUpdate !== true){
33421             this.fireEvent("beforerowremoved", this, index, record);
33422         }
33423         var bt = this.getBodyTable(), lt = this.getLockedTable();
33424         if(bt.rows[index]){
33425             bt.firstChild.removeChild(bt.rows[index]);
33426         }
33427         if(lt.rows[index]){
33428             lt.firstChild.removeChild(lt.rows[index]);
33429         }
33430         if(isUpdate !== true){
33431             this.stripeRows(index);
33432             this.syncRowHeights(index, index);
33433             this.layout();
33434             this.fireEvent("rowremoved", this, index, record);
33435         }
33436     },
33437
33438     onLoad : function(){
33439         this.scrollToTop();
33440     },
33441
33442     /**
33443      * Scrolls the grid to the top
33444      */
33445     scrollToTop : function(){
33446         if(this.scroller){
33447             this.scroller.dom.scrollTop = 0;
33448             this.syncScroll();
33449         }
33450     },
33451
33452     /**
33453      * Gets a panel in the header of the grid that can be used for toolbars etc.
33454      * After modifying the contents of this panel a call to grid.autoSize() may be
33455      * required to register any changes in size.
33456      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33457      * @return Roo.Element
33458      */
33459     getHeaderPanel : function(doShow){
33460         if(doShow){
33461             this.headerPanel.show();
33462         }
33463         return this.headerPanel;
33464     },
33465
33466     /**
33467      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33468      * After modifying the contents of this panel a call to grid.autoSize() may be
33469      * required to register any changes in size.
33470      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33471      * @return Roo.Element
33472      */
33473     getFooterPanel : function(doShow){
33474         if(doShow){
33475             this.footerPanel.show();
33476         }
33477         return this.footerPanel;
33478     },
33479
33480     initElements : function(){
33481         var E = Roo.Element;
33482         var el = this.grid.getGridEl().dom.firstChild;
33483         var cs = el.childNodes;
33484
33485         this.el = new E(el);
33486         
33487          this.focusEl = new E(el.firstChild);
33488         this.focusEl.swallowEvent("click", true);
33489         
33490         this.headerPanel = new E(cs[1]);
33491         this.headerPanel.enableDisplayMode("block");
33492
33493         this.scroller = new E(cs[2]);
33494         this.scrollSizer = new E(this.scroller.dom.firstChild);
33495
33496         this.lockedWrap = new E(cs[3]);
33497         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33498         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33499
33500         this.mainWrap = new E(cs[4]);
33501         this.mainHd = new E(this.mainWrap.dom.firstChild);
33502         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33503
33504         this.footerPanel = new E(cs[5]);
33505         this.footerPanel.enableDisplayMode("block");
33506
33507         this.resizeProxy = new E(cs[6]);
33508
33509         this.headerSelector = String.format(
33510            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33511            this.lockedHd.id, this.mainHd.id
33512         );
33513
33514         this.splitterSelector = String.format(
33515            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33516            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33517         );
33518     },
33519     idToCssName : function(s)
33520     {
33521         return s.replace(/[^a-z0-9]+/ig, '-');
33522     },
33523
33524     getHeaderCell : function(index){
33525         return Roo.DomQuery.select(this.headerSelector)[index];
33526     },
33527
33528     getHeaderCellMeasure : function(index){
33529         return this.getHeaderCell(index).firstChild;
33530     },
33531
33532     getHeaderCellText : function(index){
33533         return this.getHeaderCell(index).firstChild.firstChild;
33534     },
33535
33536     getLockedTable : function(){
33537         return this.lockedBody.dom.firstChild;
33538     },
33539
33540     getBodyTable : function(){
33541         return this.mainBody.dom.firstChild;
33542     },
33543
33544     getLockedRow : function(index){
33545         return this.getLockedTable().rows[index];
33546     },
33547
33548     getRow : function(index){
33549         return this.getBodyTable().rows[index];
33550     },
33551
33552     getRowComposite : function(index){
33553         if(!this.rowEl){
33554             this.rowEl = new Roo.CompositeElementLite();
33555         }
33556         var els = [], lrow, mrow;
33557         if(lrow = this.getLockedRow(index)){
33558             els.push(lrow);
33559         }
33560         if(mrow = this.getRow(index)){
33561             els.push(mrow);
33562         }
33563         this.rowEl.elements = els;
33564         return this.rowEl;
33565     },
33566     /**
33567      * Gets the 'td' of the cell
33568      * 
33569      * @param {Integer} rowIndex row to select
33570      * @param {Integer} colIndex column to select
33571      * 
33572      * @return {Object} 
33573      */
33574     getCell : function(rowIndex, colIndex){
33575         var locked = this.cm.getLockedCount();
33576         var source;
33577         if(colIndex < locked){
33578             source = this.lockedBody.dom.firstChild;
33579         }else{
33580             source = this.mainBody.dom.firstChild;
33581             colIndex -= locked;
33582         }
33583         return source.rows[rowIndex].childNodes[colIndex];
33584     },
33585
33586     getCellText : function(rowIndex, colIndex){
33587         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33588     },
33589
33590     getCellBox : function(cell){
33591         var b = this.fly(cell).getBox();
33592         if(Roo.isOpera){ // opera fails to report the Y
33593             b.y = cell.offsetTop + this.mainBody.getY();
33594         }
33595         return b;
33596     },
33597
33598     getCellIndex : function(cell){
33599         var id = String(cell.className).match(this.cellRE);
33600         if(id){
33601             return parseInt(id[1], 10);
33602         }
33603         return 0;
33604     },
33605
33606     findHeaderIndex : function(n){
33607         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33608         return r ? this.getCellIndex(r) : false;
33609     },
33610
33611     findHeaderCell : function(n){
33612         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33613         return r ? r : false;
33614     },
33615
33616     findRowIndex : function(n){
33617         if(!n){
33618             return false;
33619         }
33620         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33621         return r ? r.rowIndex : false;
33622     },
33623
33624     findCellIndex : function(node){
33625         var stop = this.el.dom;
33626         while(node && node != stop){
33627             if(this.findRE.test(node.className)){
33628                 return this.getCellIndex(node);
33629             }
33630             node = node.parentNode;
33631         }
33632         return false;
33633     },
33634
33635     getColumnId : function(index){
33636         return this.cm.getColumnId(index);
33637     },
33638
33639     getSplitters : function()
33640     {
33641         if(this.splitterSelector){
33642            return Roo.DomQuery.select(this.splitterSelector);
33643         }else{
33644             return null;
33645       }
33646     },
33647
33648     getSplitter : function(index){
33649         return this.getSplitters()[index];
33650     },
33651
33652     onRowOver : function(e, t){
33653         var row;
33654         if((row = this.findRowIndex(t)) !== false){
33655             this.getRowComposite(row).addClass("x-grid-row-over");
33656         }
33657     },
33658
33659     onRowOut : function(e, t){
33660         var row;
33661         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33662             this.getRowComposite(row).removeClass("x-grid-row-over");
33663         }
33664     },
33665
33666     renderHeaders : function(){
33667         var cm = this.cm;
33668         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33669         var cb = [], lb = [], sb = [], lsb = [], p = {};
33670         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33671             p.cellId = "x-grid-hd-0-" + i;
33672             p.splitId = "x-grid-csplit-0-" + i;
33673             p.id = cm.getColumnId(i);
33674             p.value = cm.getColumnHeader(i) || "";
33675             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33676             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33677             if(!cm.isLocked(i)){
33678                 cb[cb.length] = ct.apply(p);
33679                 sb[sb.length] = st.apply(p);
33680             }else{
33681                 lb[lb.length] = ct.apply(p);
33682                 lsb[lsb.length] = st.apply(p);
33683             }
33684         }
33685         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33686                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33687     },
33688
33689     updateHeaders : function(){
33690         var html = this.renderHeaders();
33691         this.lockedHd.update(html[0]);
33692         this.mainHd.update(html[1]);
33693     },
33694
33695     /**
33696      * Focuses the specified row.
33697      * @param {Number} row The row index
33698      */
33699     focusRow : function(row)
33700     {
33701         //Roo.log('GridView.focusRow');
33702         var x = this.scroller.dom.scrollLeft;
33703         this.focusCell(row, 0, false);
33704         this.scroller.dom.scrollLeft = x;
33705     },
33706
33707     /**
33708      * Focuses the specified cell.
33709      * @param {Number} row The row index
33710      * @param {Number} col The column index
33711      * @param {Boolean} hscroll false to disable horizontal scrolling
33712      */
33713     focusCell : function(row, col, hscroll)
33714     {
33715         //Roo.log('GridView.focusCell');
33716         var el = this.ensureVisible(row, col, hscroll);
33717         this.focusEl.alignTo(el, "tl-tl");
33718         if(Roo.isGecko){
33719             this.focusEl.focus();
33720         }else{
33721             this.focusEl.focus.defer(1, this.focusEl);
33722         }
33723     },
33724
33725     /**
33726      * Scrolls the specified cell into view
33727      * @param {Number} row The row index
33728      * @param {Number} col The column index
33729      * @param {Boolean} hscroll false to disable horizontal scrolling
33730      */
33731     ensureVisible : function(row, col, hscroll)
33732     {
33733         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33734         //return null; //disable for testing.
33735         if(typeof row != "number"){
33736             row = row.rowIndex;
33737         }
33738         if(row < 0 && row >= this.ds.getCount()){
33739             return  null;
33740         }
33741         col = (col !== undefined ? col : 0);
33742         var cm = this.grid.colModel;
33743         while(cm.isHidden(col)){
33744             col++;
33745         }
33746
33747         var el = this.getCell(row, col);
33748         if(!el){
33749             return null;
33750         }
33751         var c = this.scroller.dom;
33752
33753         var ctop = parseInt(el.offsetTop, 10);
33754         var cleft = parseInt(el.offsetLeft, 10);
33755         var cbot = ctop + el.offsetHeight;
33756         var cright = cleft + el.offsetWidth;
33757         
33758         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33759         var stop = parseInt(c.scrollTop, 10);
33760         var sleft = parseInt(c.scrollLeft, 10);
33761         var sbot = stop + ch;
33762         var sright = sleft + c.clientWidth;
33763         /*
33764         Roo.log('GridView.ensureVisible:' +
33765                 ' ctop:' + ctop +
33766                 ' c.clientHeight:' + c.clientHeight +
33767                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33768                 ' stop:' + stop +
33769                 ' cbot:' + cbot +
33770                 ' sbot:' + sbot +
33771                 ' ch:' + ch  
33772                 );
33773         */
33774         if(ctop < stop){
33775             c.scrollTop = ctop;
33776             //Roo.log("set scrolltop to ctop DISABLE?");
33777         }else if(cbot > sbot){
33778             //Roo.log("set scrolltop to cbot-ch");
33779             c.scrollTop = cbot-ch;
33780         }
33781         
33782         if(hscroll !== false){
33783             if(cleft < sleft){
33784                 c.scrollLeft = cleft;
33785             }else if(cright > sright){
33786                 c.scrollLeft = cright-c.clientWidth;
33787             }
33788         }
33789          
33790         return el;
33791     },
33792
33793     updateColumns : function(){
33794         this.grid.stopEditing();
33795         var cm = this.grid.colModel, colIds = this.getColumnIds();
33796         //var totalWidth = cm.getTotalWidth();
33797         var pos = 0;
33798         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33799             //if(cm.isHidden(i)) continue;
33800             var w = cm.getColumnWidth(i);
33801             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33802             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33803         }
33804         this.updateSplitters();
33805     },
33806
33807     generateRules : function(cm){
33808         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33809         Roo.util.CSS.removeStyleSheet(rulesId);
33810         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33811             var cid = cm.getColumnId(i);
33812             var align = '';
33813             if(cm.config[i].align){
33814                 align = 'text-align:'+cm.config[i].align+';';
33815             }
33816             var hidden = '';
33817             if(cm.isHidden(i)){
33818                 hidden = 'display:none;';
33819             }
33820             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33821             ruleBuf.push(
33822                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33823                     this.hdSelector, cid, " {\n", align, width, "}\n",
33824                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33825                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33826         }
33827         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33828     },
33829
33830     updateSplitters : function(){
33831         var cm = this.cm, s = this.getSplitters();
33832         if(s){ // splitters not created yet
33833             var pos = 0, locked = true;
33834             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33835                 if(cm.isHidden(i)) {
33836                     continue;
33837                 }
33838                 var w = cm.getColumnWidth(i); // make sure it's a number
33839                 if(!cm.isLocked(i) && locked){
33840                     pos = 0;
33841                     locked = false;
33842                 }
33843                 pos += w;
33844                 s[i].style.left = (pos-this.splitOffset) + "px";
33845             }
33846         }
33847     },
33848
33849     handleHiddenChange : function(colModel, colIndex, hidden){
33850         if(hidden){
33851             this.hideColumn(colIndex);
33852         }else{
33853             this.unhideColumn(colIndex);
33854         }
33855     },
33856
33857     hideColumn : function(colIndex){
33858         var cid = this.getColumnId(colIndex);
33859         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33860         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33861         if(Roo.isSafari){
33862             this.updateHeaders();
33863         }
33864         this.updateSplitters();
33865         this.layout();
33866     },
33867
33868     unhideColumn : function(colIndex){
33869         var cid = this.getColumnId(colIndex);
33870         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33871         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33872
33873         if(Roo.isSafari){
33874             this.updateHeaders();
33875         }
33876         this.updateSplitters();
33877         this.layout();
33878     },
33879
33880     insertRows : function(dm, firstRow, lastRow, isUpdate){
33881         if(firstRow == 0 && lastRow == dm.getCount()-1){
33882             this.refresh();
33883         }else{
33884             if(!isUpdate){
33885                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33886             }
33887             var s = this.getScrollState();
33888             var markup = this.renderRows(firstRow, lastRow);
33889             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33890             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33891             this.restoreScroll(s);
33892             if(!isUpdate){
33893                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33894                 this.syncRowHeights(firstRow, lastRow);
33895                 this.stripeRows(firstRow);
33896                 this.layout();
33897             }
33898         }
33899     },
33900
33901     bufferRows : function(markup, target, index){
33902         var before = null, trows = target.rows, tbody = target.tBodies[0];
33903         if(index < trows.length){
33904             before = trows[index];
33905         }
33906         var b = document.createElement("div");
33907         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33908         var rows = b.firstChild.rows;
33909         for(var i = 0, len = rows.length; i < len; i++){
33910             if(before){
33911                 tbody.insertBefore(rows[0], before);
33912             }else{
33913                 tbody.appendChild(rows[0]);
33914             }
33915         }
33916         b.innerHTML = "";
33917         b = null;
33918     },
33919
33920     deleteRows : function(dm, firstRow, lastRow){
33921         if(dm.getRowCount()<1){
33922             this.fireEvent("beforerefresh", this);
33923             this.mainBody.update("");
33924             this.lockedBody.update("");
33925             this.fireEvent("refresh", this);
33926         }else{
33927             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33928             var bt = this.getBodyTable();
33929             var tbody = bt.firstChild;
33930             var rows = bt.rows;
33931             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33932                 tbody.removeChild(rows[firstRow]);
33933             }
33934             this.stripeRows(firstRow);
33935             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33936         }
33937     },
33938
33939     updateRows : function(dataSource, firstRow, lastRow){
33940         var s = this.getScrollState();
33941         this.refresh();
33942         this.restoreScroll(s);
33943     },
33944
33945     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33946         if(!noRefresh){
33947            this.refresh();
33948         }
33949         this.updateHeaderSortState();
33950     },
33951
33952     getScrollState : function(){
33953         
33954         var sb = this.scroller.dom;
33955         return {left: sb.scrollLeft, top: sb.scrollTop};
33956     },
33957
33958     stripeRows : function(startRow){
33959         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33960             return;
33961         }
33962         startRow = startRow || 0;
33963         var rows = this.getBodyTable().rows;
33964         var lrows = this.getLockedTable().rows;
33965         var cls = ' x-grid-row-alt ';
33966         for(var i = startRow, len = rows.length; i < len; i++){
33967             var row = rows[i], lrow = lrows[i];
33968             var isAlt = ((i+1) % 2 == 0);
33969             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33970             if(isAlt == hasAlt){
33971                 continue;
33972             }
33973             if(isAlt){
33974                 row.className += " x-grid-row-alt";
33975             }else{
33976                 row.className = row.className.replace("x-grid-row-alt", "");
33977             }
33978             if(lrow){
33979                 lrow.className = row.className;
33980             }
33981         }
33982     },
33983
33984     restoreScroll : function(state){
33985         //Roo.log('GridView.restoreScroll');
33986         var sb = this.scroller.dom;
33987         sb.scrollLeft = state.left;
33988         sb.scrollTop = state.top;
33989         this.syncScroll();
33990     },
33991
33992     syncScroll : function(){
33993         //Roo.log('GridView.syncScroll');
33994         var sb = this.scroller.dom;
33995         var sh = this.mainHd.dom;
33996         var bs = this.mainBody.dom;
33997         var lv = this.lockedBody.dom;
33998         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33999         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34000     },
34001
34002     handleScroll : function(e){
34003         this.syncScroll();
34004         var sb = this.scroller.dom;
34005         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34006         e.stopEvent();
34007     },
34008
34009     handleWheel : function(e){
34010         var d = e.getWheelDelta();
34011         this.scroller.dom.scrollTop -= d*22;
34012         // set this here to prevent jumpy scrolling on large tables
34013         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34014         e.stopEvent();
34015     },
34016
34017     renderRows : function(startRow, endRow){
34018         // pull in all the crap needed to render rows
34019         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34020         var colCount = cm.getColumnCount();
34021
34022         if(ds.getCount() < 1){
34023             return ["", ""];
34024         }
34025
34026         // build a map for all the columns
34027         var cs = [];
34028         for(var i = 0; i < colCount; i++){
34029             var name = cm.getDataIndex(i);
34030             cs[i] = {
34031                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34032                 renderer : cm.getRenderer(i),
34033                 id : cm.getColumnId(i),
34034                 locked : cm.isLocked(i),
34035                 has_editor : cm.isCellEditable(i)
34036             };
34037         }
34038
34039         startRow = startRow || 0;
34040         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34041
34042         // records to render
34043         var rs = ds.getRange(startRow, endRow);
34044
34045         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34046     },
34047
34048     // As much as I hate to duplicate code, this was branched because FireFox really hates
34049     // [].join("") on strings. The performance difference was substantial enough to
34050     // branch this function
34051     doRender : Roo.isGecko ?
34052             function(cs, rs, ds, startRow, colCount, stripe){
34053                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34054                 // buffers
34055                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34056                 
34057                 var hasListener = this.grid.hasListener('rowclass');
34058                 var rowcfg = {};
34059                 for(var j = 0, len = rs.length; j < len; j++){
34060                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34061                     for(var i = 0; i < colCount; i++){
34062                         c = cs[i];
34063                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34064                         p.id = c.id;
34065                         p.css = p.attr = "";
34066                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34067                         if(p.value == undefined || p.value === "") {
34068                             p.value = "&#160;";
34069                         }
34070                         if(c.has_editor){
34071                             p.css += ' x-grid-editable-cell';
34072                         }
34073                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34074                             p.css +=  ' x-grid-dirty-cell';
34075                         }
34076                         var markup = ct.apply(p);
34077                         if(!c.locked){
34078                             cb+= markup;
34079                         }else{
34080                             lcb+= markup;
34081                         }
34082                     }
34083                     var alt = [];
34084                     if(stripe && ((rowIndex+1) % 2 == 0)){
34085                         alt.push("x-grid-row-alt")
34086                     }
34087                     if(r.dirty){
34088                         alt.push(  " x-grid-dirty-row");
34089                     }
34090                     rp.cells = lcb;
34091                     if(this.getRowClass){
34092                         alt.push(this.getRowClass(r, rowIndex));
34093                     }
34094                     if (hasListener) {
34095                         rowcfg = {
34096                              
34097                             record: r,
34098                             rowIndex : rowIndex,
34099                             rowClass : ''
34100                         };
34101                         this.grid.fireEvent('rowclass', this, rowcfg);
34102                         alt.push(rowcfg.rowClass);
34103                     }
34104                     rp.alt = alt.join(" ");
34105                     lbuf+= rt.apply(rp);
34106                     rp.cells = cb;
34107                     buf+=  rt.apply(rp);
34108                 }
34109                 return [lbuf, buf];
34110             } :
34111             function(cs, rs, ds, startRow, colCount, stripe){
34112                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34113                 // buffers
34114                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34115                 var hasListener = this.grid.hasListener('rowclass');
34116  
34117                 var rowcfg = {};
34118                 for(var j = 0, len = rs.length; j < len; j++){
34119                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34120                     for(var i = 0; i < colCount; i++){
34121                         c = cs[i];
34122                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34123                         p.id = c.id;
34124                         p.css = p.attr = "";
34125                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34126                         if(p.value == undefined || p.value === "") {
34127                             p.value = "&#160;";
34128                         }
34129                         //Roo.log(c);
34130                          if(c.has_editor){
34131                             p.css += ' x-grid-editable-cell';
34132                         }
34133                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34134                             p.css += ' x-grid-dirty-cell' 
34135                         }
34136                         
34137                         var markup = ct.apply(p);
34138                         if(!c.locked){
34139                             cb[cb.length] = markup;
34140                         }else{
34141                             lcb[lcb.length] = markup;
34142                         }
34143                     }
34144                     var alt = [];
34145                     if(stripe && ((rowIndex+1) % 2 == 0)){
34146                         alt.push( "x-grid-row-alt");
34147                     }
34148                     if(r.dirty){
34149                         alt.push(" x-grid-dirty-row");
34150                     }
34151                     rp.cells = lcb;
34152                     if(this.getRowClass){
34153                         alt.push( this.getRowClass(r, rowIndex));
34154                     }
34155                     if (hasListener) {
34156                         rowcfg = {
34157                              
34158                             record: r,
34159                             rowIndex : rowIndex,
34160                             rowClass : ''
34161                         };
34162                         this.grid.fireEvent('rowclass', this, rowcfg);
34163                         alt.push(rowcfg.rowClass);
34164                     }
34165                     
34166                     rp.alt = alt.join(" ");
34167                     rp.cells = lcb.join("");
34168                     lbuf[lbuf.length] = rt.apply(rp);
34169                     rp.cells = cb.join("");
34170                     buf[buf.length] =  rt.apply(rp);
34171                 }
34172                 return [lbuf.join(""), buf.join("")];
34173             },
34174
34175     renderBody : function(){
34176         var markup = this.renderRows();
34177         var bt = this.templates.body;
34178         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34179     },
34180
34181     /**
34182      * Refreshes the grid
34183      * @param {Boolean} headersToo
34184      */
34185     refresh : function(headersToo){
34186         this.fireEvent("beforerefresh", this);
34187         this.grid.stopEditing();
34188         var result = this.renderBody();
34189         this.lockedBody.update(result[0]);
34190         this.mainBody.update(result[1]);
34191         if(headersToo === true){
34192             this.updateHeaders();
34193             this.updateColumns();
34194             this.updateSplitters();
34195             this.updateHeaderSortState();
34196         }
34197         this.syncRowHeights();
34198         this.layout();
34199         this.fireEvent("refresh", this);
34200     },
34201
34202     handleColumnMove : function(cm, oldIndex, newIndex){
34203         this.indexMap = null;
34204         var s = this.getScrollState();
34205         this.refresh(true);
34206         this.restoreScroll(s);
34207         this.afterMove(newIndex);
34208     },
34209
34210     afterMove : function(colIndex){
34211         if(this.enableMoveAnim && Roo.enableFx){
34212             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34213         }
34214         // if multisort - fix sortOrder, and reload..
34215         if (this.grid.dataSource.multiSort) {
34216             // the we can call sort again..
34217             var dm = this.grid.dataSource;
34218             var cm = this.grid.colModel;
34219             var so = [];
34220             for(var i = 0; i < cm.config.length; i++ ) {
34221                 
34222                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34223                     continue; // dont' bother, it's not in sort list or being set.
34224                 }
34225                 
34226                 so.push(cm.config[i].dataIndex);
34227             };
34228             dm.sortOrder = so;
34229             dm.load(dm.lastOptions);
34230             
34231             
34232         }
34233         
34234     },
34235
34236     updateCell : function(dm, rowIndex, dataIndex){
34237         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34238         if(typeof colIndex == "undefined"){ // not present in grid
34239             return;
34240         }
34241         var cm = this.grid.colModel;
34242         var cell = this.getCell(rowIndex, colIndex);
34243         var cellText = this.getCellText(rowIndex, colIndex);
34244
34245         var p = {
34246             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34247             id : cm.getColumnId(colIndex),
34248             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34249         };
34250         var renderer = cm.getRenderer(colIndex);
34251         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34252         if(typeof val == "undefined" || val === "") {
34253             val = "&#160;";
34254         }
34255         cellText.innerHTML = val;
34256         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34257         this.syncRowHeights(rowIndex, rowIndex);
34258     },
34259
34260     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34261         var maxWidth = 0;
34262         if(this.grid.autoSizeHeaders){
34263             var h = this.getHeaderCellMeasure(colIndex);
34264             maxWidth = Math.max(maxWidth, h.scrollWidth);
34265         }
34266         var tb, index;
34267         if(this.cm.isLocked(colIndex)){
34268             tb = this.getLockedTable();
34269             index = colIndex;
34270         }else{
34271             tb = this.getBodyTable();
34272             index = colIndex - this.cm.getLockedCount();
34273         }
34274         if(tb && tb.rows){
34275             var rows = tb.rows;
34276             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34277             for(var i = 0; i < stopIndex; i++){
34278                 var cell = rows[i].childNodes[index].firstChild;
34279                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34280             }
34281         }
34282         return maxWidth + /*margin for error in IE*/ 5;
34283     },
34284     /**
34285      * Autofit a column to its content.
34286      * @param {Number} colIndex
34287      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34288      */
34289      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34290          if(this.cm.isHidden(colIndex)){
34291              return; // can't calc a hidden column
34292          }
34293         if(forceMinSize){
34294             var cid = this.cm.getColumnId(colIndex);
34295             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34296            if(this.grid.autoSizeHeaders){
34297                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34298            }
34299         }
34300         var newWidth = this.calcColumnWidth(colIndex);
34301         this.cm.setColumnWidth(colIndex,
34302             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34303         if(!suppressEvent){
34304             this.grid.fireEvent("columnresize", colIndex, newWidth);
34305         }
34306     },
34307
34308     /**
34309      * Autofits all columns to their content and then expands to fit any extra space in the grid
34310      */
34311      autoSizeColumns : function(){
34312         var cm = this.grid.colModel;
34313         var colCount = cm.getColumnCount();
34314         for(var i = 0; i < colCount; i++){
34315             this.autoSizeColumn(i, true, true);
34316         }
34317         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34318             this.fitColumns();
34319         }else{
34320             this.updateColumns();
34321             this.layout();
34322         }
34323     },
34324
34325     /**
34326      * Autofits all columns to the grid's width proportionate with their current size
34327      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34328      */
34329     fitColumns : function(reserveScrollSpace){
34330         var cm = this.grid.colModel;
34331         var colCount = cm.getColumnCount();
34332         var cols = [];
34333         var width = 0;
34334         var i, w;
34335         for (i = 0; i < colCount; i++){
34336             if(!cm.isHidden(i) && !cm.isFixed(i)){
34337                 w = cm.getColumnWidth(i);
34338                 cols.push(i);
34339                 cols.push(w);
34340                 width += w;
34341             }
34342         }
34343         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34344         if(reserveScrollSpace){
34345             avail -= 17;
34346         }
34347         var frac = (avail - cm.getTotalWidth())/width;
34348         while (cols.length){
34349             w = cols.pop();
34350             i = cols.pop();
34351             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34352         }
34353         this.updateColumns();
34354         this.layout();
34355     },
34356
34357     onRowSelect : function(rowIndex){
34358         var row = this.getRowComposite(rowIndex);
34359         row.addClass("x-grid-row-selected");
34360     },
34361
34362     onRowDeselect : function(rowIndex){
34363         var row = this.getRowComposite(rowIndex);
34364         row.removeClass("x-grid-row-selected");
34365     },
34366
34367     onCellSelect : function(row, col){
34368         var cell = this.getCell(row, col);
34369         if(cell){
34370             Roo.fly(cell).addClass("x-grid-cell-selected");
34371         }
34372     },
34373
34374     onCellDeselect : function(row, col){
34375         var cell = this.getCell(row, col);
34376         if(cell){
34377             Roo.fly(cell).removeClass("x-grid-cell-selected");
34378         }
34379     },
34380
34381     updateHeaderSortState : function(){
34382         
34383         // sort state can be single { field: xxx, direction : yyy}
34384         // or   { xxx=>ASC , yyy : DESC ..... }
34385         
34386         var mstate = {};
34387         if (!this.ds.multiSort) { 
34388             var state = this.ds.getSortState();
34389             if(!state){
34390                 return;
34391             }
34392             mstate[state.field] = state.direction;
34393             // FIXME... - this is not used here.. but might be elsewhere..
34394             this.sortState = state;
34395             
34396         } else {
34397             mstate = this.ds.sortToggle;
34398         }
34399         //remove existing sort classes..
34400         
34401         var sc = this.sortClasses;
34402         var hds = this.el.select(this.headerSelector).removeClass(sc);
34403         
34404         for(var f in mstate) {
34405         
34406             var sortColumn = this.cm.findColumnIndex(f);
34407             
34408             if(sortColumn != -1){
34409                 var sortDir = mstate[f];        
34410                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34411             }
34412         }
34413         
34414          
34415         
34416     },
34417
34418
34419     handleHeaderClick : function(g, index,e){
34420         
34421         Roo.log("header click");
34422         
34423         if (Roo.isTouch) {
34424             // touch events on header are handled by context
34425             this.handleHdCtx(g,index,e);
34426             return;
34427         }
34428         
34429         
34430         if(this.headersDisabled){
34431             return;
34432         }
34433         var dm = g.dataSource, cm = g.colModel;
34434         if(!cm.isSortable(index)){
34435             return;
34436         }
34437         g.stopEditing();
34438         
34439         if (dm.multiSort) {
34440             // update the sortOrder
34441             var so = [];
34442             for(var i = 0; i < cm.config.length; i++ ) {
34443                 
34444                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34445                     continue; // dont' bother, it's not in sort list or being set.
34446                 }
34447                 
34448                 so.push(cm.config[i].dataIndex);
34449             };
34450             dm.sortOrder = so;
34451         }
34452         
34453         
34454         dm.sort(cm.getDataIndex(index));
34455     },
34456
34457
34458     destroy : function(){
34459         if(this.colMenu){
34460             this.colMenu.removeAll();
34461             Roo.menu.MenuMgr.unregister(this.colMenu);
34462             this.colMenu.getEl().remove();
34463             delete this.colMenu;
34464         }
34465         if(this.hmenu){
34466             this.hmenu.removeAll();
34467             Roo.menu.MenuMgr.unregister(this.hmenu);
34468             this.hmenu.getEl().remove();
34469             delete this.hmenu;
34470         }
34471         if(this.grid.enableColumnMove){
34472             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34473             if(dds){
34474                 for(var dd in dds){
34475                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34476                         var elid = dds[dd].dragElId;
34477                         dds[dd].unreg();
34478                         Roo.get(elid).remove();
34479                     } else if(dds[dd].config.isTarget){
34480                         dds[dd].proxyTop.remove();
34481                         dds[dd].proxyBottom.remove();
34482                         dds[dd].unreg();
34483                     }
34484                     if(Roo.dd.DDM.locationCache[dd]){
34485                         delete Roo.dd.DDM.locationCache[dd];
34486                     }
34487                 }
34488                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34489             }
34490         }
34491         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34492         this.bind(null, null);
34493         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34494     },
34495
34496     handleLockChange : function(){
34497         this.refresh(true);
34498     },
34499
34500     onDenyColumnLock : function(){
34501
34502     },
34503
34504     onDenyColumnHide : function(){
34505
34506     },
34507
34508     handleHdMenuClick : function(item){
34509         var index = this.hdCtxIndex;
34510         var cm = this.cm, ds = this.ds;
34511         switch(item.id){
34512             case "asc":
34513                 ds.sort(cm.getDataIndex(index), "ASC");
34514                 break;
34515             case "desc":
34516                 ds.sort(cm.getDataIndex(index), "DESC");
34517                 break;
34518             case "lock":
34519                 var lc = cm.getLockedCount();
34520                 if(cm.getColumnCount(true) <= lc+1){
34521                     this.onDenyColumnLock();
34522                     return;
34523                 }
34524                 if(lc != index){
34525                     cm.setLocked(index, true, true);
34526                     cm.moveColumn(index, lc);
34527                     this.grid.fireEvent("columnmove", index, lc);
34528                 }else{
34529                     cm.setLocked(index, true);
34530                 }
34531             break;
34532             case "unlock":
34533                 var lc = cm.getLockedCount();
34534                 if((lc-1) != index){
34535                     cm.setLocked(index, false, true);
34536                     cm.moveColumn(index, lc-1);
34537                     this.grid.fireEvent("columnmove", index, lc-1);
34538                 }else{
34539                     cm.setLocked(index, false);
34540                 }
34541             break;
34542             case 'wider': // used to expand cols on touch..
34543             case 'narrow':
34544                 var cw = cm.getColumnWidth(index);
34545                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34546                 cw = Math.max(0, cw);
34547                 cw = Math.min(cw,4000);
34548                 cm.setColumnWidth(index, cw);
34549                 break;
34550                 
34551             default:
34552                 index = cm.getIndexById(item.id.substr(4));
34553                 if(index != -1){
34554                     if(item.checked && cm.getColumnCount(true) <= 1){
34555                         this.onDenyColumnHide();
34556                         return false;
34557                     }
34558                     cm.setHidden(index, item.checked);
34559                 }
34560         }
34561         return true;
34562     },
34563
34564     beforeColMenuShow : function(){
34565         var cm = this.cm,  colCount = cm.getColumnCount();
34566         this.colMenu.removeAll();
34567         for(var i = 0; i < colCount; i++){
34568             this.colMenu.add(new Roo.menu.CheckItem({
34569                 id: "col-"+cm.getColumnId(i),
34570                 text: cm.getColumnHeader(i),
34571                 checked: !cm.isHidden(i),
34572                 hideOnClick:false
34573             }));
34574         }
34575     },
34576
34577     handleHdCtx : function(g, index, e){
34578         e.stopEvent();
34579         var hd = this.getHeaderCell(index);
34580         this.hdCtxIndex = index;
34581         var ms = this.hmenu.items, cm = this.cm;
34582         ms.get("asc").setDisabled(!cm.isSortable(index));
34583         ms.get("desc").setDisabled(!cm.isSortable(index));
34584         if(this.grid.enableColLock !== false){
34585             ms.get("lock").setDisabled(cm.isLocked(index));
34586             ms.get("unlock").setDisabled(!cm.isLocked(index));
34587         }
34588         this.hmenu.show(hd, "tl-bl");
34589     },
34590
34591     handleHdOver : function(e){
34592         var hd = this.findHeaderCell(e.getTarget());
34593         if(hd && !this.headersDisabled){
34594             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34595                this.fly(hd).addClass("x-grid-hd-over");
34596             }
34597         }
34598     },
34599
34600     handleHdOut : function(e){
34601         var hd = this.findHeaderCell(e.getTarget());
34602         if(hd){
34603             this.fly(hd).removeClass("x-grid-hd-over");
34604         }
34605     },
34606
34607     handleSplitDblClick : function(e, t){
34608         var i = this.getCellIndex(t);
34609         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34610             this.autoSizeColumn(i, true);
34611             this.layout();
34612         }
34613     },
34614
34615     render : function(){
34616
34617         var cm = this.cm;
34618         var colCount = cm.getColumnCount();
34619
34620         if(this.grid.monitorWindowResize === true){
34621             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34622         }
34623         var header = this.renderHeaders();
34624         var body = this.templates.body.apply({rows:""});
34625         var html = this.templates.master.apply({
34626             lockedBody: body,
34627             body: body,
34628             lockedHeader: header[0],
34629             header: header[1]
34630         });
34631
34632         //this.updateColumns();
34633
34634         this.grid.getGridEl().dom.innerHTML = html;
34635
34636         this.initElements();
34637         
34638         // a kludge to fix the random scolling effect in webkit
34639         this.el.on("scroll", function() {
34640             this.el.dom.scrollTop=0; // hopefully not recursive..
34641         },this);
34642
34643         this.scroller.on("scroll", this.handleScroll, this);
34644         this.lockedBody.on("mousewheel", this.handleWheel, this);
34645         this.mainBody.on("mousewheel", this.handleWheel, this);
34646
34647         this.mainHd.on("mouseover", this.handleHdOver, this);
34648         this.mainHd.on("mouseout", this.handleHdOut, this);
34649         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34650                 {delegate: "."+this.splitClass});
34651
34652         this.lockedHd.on("mouseover", this.handleHdOver, this);
34653         this.lockedHd.on("mouseout", this.handleHdOut, this);
34654         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34655                 {delegate: "."+this.splitClass});
34656
34657         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34658             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34659         }
34660
34661         this.updateSplitters();
34662
34663         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34664             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34665             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34666         }
34667
34668         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34669             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34670             this.hmenu.add(
34671                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34672                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34673             );
34674             if(this.grid.enableColLock !== false){
34675                 this.hmenu.add('-',
34676                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34677                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34678                 );
34679             }
34680             if (Roo.isTouch) {
34681                  this.hmenu.add('-',
34682                     {id:"wider", text: this.columnsWiderText},
34683                     {id:"narrow", text: this.columnsNarrowText }
34684                 );
34685                 
34686                  
34687             }
34688             
34689             if(this.grid.enableColumnHide !== false){
34690
34691                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34692                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34693                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34694
34695                 this.hmenu.add('-',
34696                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34697                 );
34698             }
34699             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34700
34701             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34702         }
34703
34704         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34705             this.dd = new Roo.grid.GridDragZone(this.grid, {
34706                 ddGroup : this.grid.ddGroup || 'GridDD'
34707             });
34708             
34709         }
34710
34711         /*
34712         for(var i = 0; i < colCount; i++){
34713             if(cm.isHidden(i)){
34714                 this.hideColumn(i);
34715             }
34716             if(cm.config[i].align){
34717                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34718                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34719             }
34720         }*/
34721         
34722         this.updateHeaderSortState();
34723
34724         this.beforeInitialResize();
34725         this.layout(true);
34726
34727         // two part rendering gives faster view to the user
34728         this.renderPhase2.defer(1, this);
34729     },
34730
34731     renderPhase2 : function(){
34732         // render the rows now
34733         this.refresh();
34734         if(this.grid.autoSizeColumns){
34735             this.autoSizeColumns();
34736         }
34737     },
34738
34739     beforeInitialResize : function(){
34740
34741     },
34742
34743     onColumnSplitterMoved : function(i, w){
34744         this.userResized = true;
34745         var cm = this.grid.colModel;
34746         cm.setColumnWidth(i, w, true);
34747         var cid = cm.getColumnId(i);
34748         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34749         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34750         this.updateSplitters();
34751         this.layout();
34752         this.grid.fireEvent("columnresize", i, w);
34753     },
34754
34755     syncRowHeights : function(startIndex, endIndex){
34756         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34757             startIndex = startIndex || 0;
34758             var mrows = this.getBodyTable().rows;
34759             var lrows = this.getLockedTable().rows;
34760             var len = mrows.length-1;
34761             endIndex = Math.min(endIndex || len, len);
34762             for(var i = startIndex; i <= endIndex; i++){
34763                 var m = mrows[i], l = lrows[i];
34764                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34765                 m.style.height = l.style.height = h + "px";
34766             }
34767         }
34768     },
34769
34770     layout : function(initialRender, is2ndPass)
34771     {
34772         var g = this.grid;
34773         var auto = g.autoHeight;
34774         var scrollOffset = 16;
34775         var c = g.getGridEl(), cm = this.cm,
34776                 expandCol = g.autoExpandColumn,
34777                 gv = this;
34778         //c.beginMeasure();
34779
34780         if(!c.dom.offsetWidth){ // display:none?
34781             if(initialRender){
34782                 this.lockedWrap.show();
34783                 this.mainWrap.show();
34784             }
34785             return;
34786         }
34787
34788         var hasLock = this.cm.isLocked(0);
34789
34790         var tbh = this.headerPanel.getHeight();
34791         var bbh = this.footerPanel.getHeight();
34792
34793         if(auto){
34794             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34795             var newHeight = ch + c.getBorderWidth("tb");
34796             if(g.maxHeight){
34797                 newHeight = Math.min(g.maxHeight, newHeight);
34798             }
34799             c.setHeight(newHeight);
34800         }
34801
34802         if(g.autoWidth){
34803             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34804         }
34805
34806         var s = this.scroller;
34807
34808         var csize = c.getSize(true);
34809
34810         this.el.setSize(csize.width, csize.height);
34811
34812         this.headerPanel.setWidth(csize.width);
34813         this.footerPanel.setWidth(csize.width);
34814
34815         var hdHeight = this.mainHd.getHeight();
34816         var vw = csize.width;
34817         var vh = csize.height - (tbh + bbh);
34818
34819         s.setSize(vw, vh);
34820
34821         var bt = this.getBodyTable();
34822         
34823         if(cm.getLockedCount() == cm.config.length){
34824             bt = this.getLockedTable();
34825         }
34826         
34827         var ltWidth = hasLock ?
34828                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34829
34830         var scrollHeight = bt.offsetHeight;
34831         var scrollWidth = ltWidth + bt.offsetWidth;
34832         var vscroll = false, hscroll = false;
34833
34834         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34835
34836         var lw = this.lockedWrap, mw = this.mainWrap;
34837         var lb = this.lockedBody, mb = this.mainBody;
34838
34839         setTimeout(function(){
34840             var t = s.dom.offsetTop;
34841             var w = s.dom.clientWidth,
34842                 h = s.dom.clientHeight;
34843
34844             lw.setTop(t);
34845             lw.setSize(ltWidth, h);
34846
34847             mw.setLeftTop(ltWidth, t);
34848             mw.setSize(w-ltWidth, h);
34849
34850             lb.setHeight(h-hdHeight);
34851             mb.setHeight(h-hdHeight);
34852
34853             if(is2ndPass !== true && !gv.userResized && expandCol){
34854                 // high speed resize without full column calculation
34855                 
34856                 var ci = cm.getIndexById(expandCol);
34857                 if (ci < 0) {
34858                     ci = cm.findColumnIndex(expandCol);
34859                 }
34860                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34861                 var expandId = cm.getColumnId(ci);
34862                 var  tw = cm.getTotalWidth(false);
34863                 var currentWidth = cm.getColumnWidth(ci);
34864                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34865                 if(currentWidth != cw){
34866                     cm.setColumnWidth(ci, cw, true);
34867                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34868                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34869                     gv.updateSplitters();
34870                     gv.layout(false, true);
34871                 }
34872             }
34873
34874             if(initialRender){
34875                 lw.show();
34876                 mw.show();
34877             }
34878             //c.endMeasure();
34879         }, 10);
34880     },
34881
34882     onWindowResize : function(){
34883         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34884             return;
34885         }
34886         this.layout();
34887     },
34888
34889     appendFooter : function(parentEl){
34890         return null;
34891     },
34892
34893     sortAscText : "Sort Ascending",
34894     sortDescText : "Sort Descending",
34895     lockText : "Lock Column",
34896     unlockText : "Unlock Column",
34897     columnsText : "Columns",
34898  
34899     columnsWiderText : "Wider",
34900     columnsNarrowText : "Thinner"
34901 });
34902
34903
34904 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34905     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34906     this.proxy.el.addClass('x-grid3-col-dd');
34907 };
34908
34909 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34910     handleMouseDown : function(e){
34911
34912     },
34913
34914     callHandleMouseDown : function(e){
34915         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34916     }
34917 });
34918 /*
34919  * Based on:
34920  * Ext JS Library 1.1.1
34921  * Copyright(c) 2006-2007, Ext JS, LLC.
34922  *
34923  * Originally Released Under LGPL - original licence link has changed is not relivant.
34924  *
34925  * Fork - LGPL
34926  * <script type="text/javascript">
34927  */
34928  /**
34929  * @extends Roo.dd.DDProxy
34930  * @class Roo.grid.SplitDragZone
34931  * Support for Column Header resizing
34932  * @constructor
34933  * @param {Object} config
34934  */
34935 // private
34936 // This is a support class used internally by the Grid components
34937 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34938     this.grid = grid;
34939     this.view = grid.getView();
34940     this.proxy = this.view.resizeProxy;
34941     Roo.grid.SplitDragZone.superclass.constructor.call(
34942         this,
34943         hd, // ID
34944         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
34945         {  // CONFIG
34946             dragElId : Roo.id(this.proxy.dom),
34947             resizeFrame:false
34948         }
34949     );
34950     
34951     this.setHandleElId(Roo.id(hd));
34952     if (hd2 !== false) {
34953         this.setOuterHandleElId(Roo.id(hd2));
34954     }
34955     
34956     this.scroll = false;
34957 };
34958 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34959     fly: Roo.Element.fly,
34960
34961     b4StartDrag : function(x, y){
34962         this.view.headersDisabled = true;
34963         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
34964                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
34965         );
34966         this.proxy.setHeight(h);
34967         
34968         // for old system colWidth really stored the actual width?
34969         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
34970         // which in reality did not work.. - it worked only for fixed sizes
34971         // for resizable we need to use actual sizes.
34972         var w = this.cm.getColumnWidth(this.cellIndex);
34973         if (!this.view.mainWrap) {
34974             // bootstrap.
34975             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
34976         }
34977         
34978         
34979         
34980         // this was w-this.grid.minColumnWidth;
34981         // doesnt really make sense? - w = thie curren width or the rendered one?
34982         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34983         this.resetConstraints();
34984         this.setXConstraint(minw, 1000);
34985         this.setYConstraint(0, 0);
34986         this.minX = x - minw;
34987         this.maxX = x + 1000;
34988         this.startPos = x;
34989         if (!this.view.mainWrap) { // this is Bootstrap code..
34990             this.getDragEl().style.display='block';
34991         }
34992         
34993         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34994     },
34995
34996
34997     handleMouseDown : function(e){
34998         ev = Roo.EventObject.setEvent(e);
34999         var t = this.fly(ev.getTarget());
35000         if(t.hasClass("x-grid-split")){
35001             this.cellIndex = this.view.getCellIndex(t.dom);
35002             this.split = t.dom;
35003             this.cm = this.grid.colModel;
35004             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35005                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35006             }
35007         }
35008     },
35009
35010     endDrag : function(e){
35011         this.view.headersDisabled = false;
35012         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35013         var diff = endX - this.startPos;
35014         // 
35015         var w = this.cm.getColumnWidth(this.cellIndex);
35016         if (!this.view.mainWrap) {
35017             w = 0;
35018         }
35019         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
35020     },
35021
35022     autoOffset : function(){
35023         this.setDelta(0,0);
35024     }
35025 });/*
35026  * Based on:
35027  * Ext JS Library 1.1.1
35028  * Copyright(c) 2006-2007, Ext JS, LLC.
35029  *
35030  * Originally Released Under LGPL - original licence link has changed is not relivant.
35031  *
35032  * Fork - LGPL
35033  * <script type="text/javascript">
35034  */
35035  
35036 // private
35037 // This is a support class used internally by the Grid components
35038 Roo.grid.GridDragZone = function(grid, config){
35039     this.view = grid.getView();
35040     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35041     if(this.view.lockedBody){
35042         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35043         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35044     }
35045     this.scroll = false;
35046     this.grid = grid;
35047     this.ddel = document.createElement('div');
35048     this.ddel.className = 'x-grid-dd-wrap';
35049 };
35050
35051 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35052     ddGroup : "GridDD",
35053
35054     getDragData : function(e){
35055         var t = Roo.lib.Event.getTarget(e);
35056         var rowIndex = this.view.findRowIndex(t);
35057         var sm = this.grid.selModel;
35058             
35059         //Roo.log(rowIndex);
35060         
35061         if (sm.getSelectedCell) {
35062             // cell selection..
35063             if (!sm.getSelectedCell()) {
35064                 return false;
35065             }
35066             if (rowIndex != sm.getSelectedCell()[0]) {
35067                 return false;
35068             }
35069         
35070         }
35071         if (sm.getSelections && sm.getSelections().length < 1) {
35072             return false;
35073         }
35074         
35075         
35076         // before it used to all dragging of unseleted... - now we dont do that.
35077         if(rowIndex !== false){
35078             
35079             // if editorgrid.. 
35080             
35081             
35082             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35083                
35084             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35085               //  
35086             //}
35087             if (e.hasModifier()){
35088                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35089             }
35090             
35091             Roo.log("getDragData");
35092             
35093             return {
35094                 grid: this.grid,
35095                 ddel: this.ddel,
35096                 rowIndex: rowIndex,
35097                 selections: sm.getSelections ? sm.getSelections() : (
35098                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
35099             };
35100         }
35101         return false;
35102     },
35103     
35104     
35105     onInitDrag : function(e){
35106         var data = this.dragData;
35107         this.ddel.innerHTML = this.grid.getDragDropText();
35108         this.proxy.update(this.ddel);
35109         // fire start drag?
35110     },
35111
35112     afterRepair : function(){
35113         this.dragging = false;
35114     },
35115
35116     getRepairXY : function(e, data){
35117         return false;
35118     },
35119
35120     onEndDrag : function(data, e){
35121         // fire end drag?
35122     },
35123
35124     onValidDrop : function(dd, e, id){
35125         // fire drag drop?
35126         this.hideProxy();
35127     },
35128
35129     beforeInvalidDrop : function(e, id){
35130
35131     }
35132 });/*
35133  * Based on:
35134  * Ext JS Library 1.1.1
35135  * Copyright(c) 2006-2007, Ext JS, LLC.
35136  *
35137  * Originally Released Under LGPL - original licence link has changed is not relivant.
35138  *
35139  * Fork - LGPL
35140  * <script type="text/javascript">
35141  */
35142  
35143
35144 /**
35145  * @class Roo.grid.ColumnModel
35146  * @extends Roo.util.Observable
35147  * This is the default implementation of a ColumnModel used by the Grid. It defines
35148  * the columns in the grid.
35149  * <br>Usage:<br>
35150  <pre><code>
35151  var colModel = new Roo.grid.ColumnModel([
35152         {header: "Ticker", width: 60, sortable: true, locked: true},
35153         {header: "Company Name", width: 150, sortable: true},
35154         {header: "Market Cap.", width: 100, sortable: true},
35155         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35156         {header: "Employees", width: 100, sortable: true, resizable: false}
35157  ]);
35158  </code></pre>
35159  * <p>
35160  
35161  * The config options listed for this class are options which may appear in each
35162  * individual column definition.
35163  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35164  * @constructor
35165  * @param {Object} config An Array of column config objects. See this class's
35166  * config objects for details.
35167 */
35168 Roo.grid.ColumnModel = function(config){
35169         /**
35170      * The config passed into the constructor
35171      */
35172     this.config = []; //config;
35173     this.lookup = {};
35174
35175     // if no id, create one
35176     // if the column does not have a dataIndex mapping,
35177     // map it to the order it is in the config
35178     for(var i = 0, len = config.length; i < len; i++){
35179         this.addColumn(config[i]);
35180         
35181     }
35182
35183     /**
35184      * The width of columns which have no width specified (defaults to 100)
35185      * @type Number
35186      */
35187     this.defaultWidth = 100;
35188
35189     /**
35190      * Default sortable of columns which have no sortable specified (defaults to false)
35191      * @type Boolean
35192      */
35193     this.defaultSortable = false;
35194
35195     this.addEvents({
35196         /**
35197              * @event widthchange
35198              * Fires when the width of a column changes.
35199              * @param {ColumnModel} this
35200              * @param {Number} columnIndex The column index
35201              * @param {Number} newWidth The new width
35202              */
35203             "widthchange": true,
35204         /**
35205              * @event headerchange
35206              * Fires when the text of a header changes.
35207              * @param {ColumnModel} this
35208              * @param {Number} columnIndex The column index
35209              * @param {Number} newText The new header text
35210              */
35211             "headerchange": true,
35212         /**
35213              * @event hiddenchange
35214              * Fires when a column is hidden or "unhidden".
35215              * @param {ColumnModel} this
35216              * @param {Number} columnIndex The column index
35217              * @param {Boolean} hidden true if hidden, false otherwise
35218              */
35219             "hiddenchange": true,
35220             /**
35221          * @event columnmoved
35222          * Fires when a column is moved.
35223          * @param {ColumnModel} this
35224          * @param {Number} oldIndex
35225          * @param {Number} newIndex
35226          */
35227         "columnmoved" : true,
35228         /**
35229          * @event columlockchange
35230          * Fires when a column's locked state is changed
35231          * @param {ColumnModel} this
35232          * @param {Number} colIndex
35233          * @param {Boolean} locked true if locked
35234          */
35235         "columnlockchange" : true
35236     });
35237     Roo.grid.ColumnModel.superclass.constructor.call(this);
35238 };
35239 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35240     /**
35241      * @cfg {String} header The header text to display in the Grid view.
35242      */
35243         /**
35244      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
35245      */
35246         /**
35247      * @cfg {String} smHeader Header at Bootsrap Small width
35248      */
35249         /**
35250      * @cfg {String} mdHeader Header at Bootsrap Medium width
35251      */
35252         /**
35253      * @cfg {String} lgHeader Header at Bootsrap Large width
35254      */
35255         /**
35256      * @cfg {String} xlHeader Header at Bootsrap extra Large width
35257      */
35258     /**
35259      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35260      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35261      * specified, the column's index is used as an index into the Record's data Array.
35262      */
35263     /**
35264      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35265      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35266      */
35267     /**
35268      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35269      * Defaults to the value of the {@link #defaultSortable} property.
35270      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35271      */
35272     /**
35273      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35274      */
35275     /**
35276      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35277      */
35278     /**
35279      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35280      */
35281     /**
35282      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35283      */
35284     /**
35285      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35286      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35287      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35288      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35289      */
35290        /**
35291      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35292      */
35293     /**
35294      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35295      */
35296     /**
35297      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35298      */
35299     /**
35300      * @cfg {String} cursor (Optional)
35301      */
35302     /**
35303      * @cfg {String} tooltip (Optional)
35304      */
35305     /**
35306      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
35307      */
35308     /**
35309      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
35310      */
35311     /**
35312      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
35313      */
35314     /**
35315      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
35316      */
35317         /**
35318      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
35319      */
35320     /**
35321      * Returns the id of the column at the specified index.
35322      * @param {Number} index The column index
35323      * @return {String} the id
35324      */
35325     getColumnId : function(index){
35326         return this.config[index].id;
35327     },
35328
35329     /**
35330      * Returns the column for a specified id.
35331      * @param {String} id The column id
35332      * @return {Object} the column
35333      */
35334     getColumnById : function(id){
35335         return this.lookup[id];
35336     },
35337
35338     
35339     /**
35340      * Returns the column Object for a specified dataIndex.
35341      * @param {String} dataIndex The column dataIndex
35342      * @return {Object|Boolean} the column or false if not found
35343      */
35344     getColumnByDataIndex: function(dataIndex){
35345         var index = this.findColumnIndex(dataIndex);
35346         return index > -1 ? this.config[index] : false;
35347     },
35348     
35349     /**
35350      * Returns the index for a specified column id.
35351      * @param {String} id The column id
35352      * @return {Number} the index, or -1 if not found
35353      */
35354     getIndexById : function(id){
35355         for(var i = 0, len = this.config.length; i < len; i++){
35356             if(this.config[i].id == id){
35357                 return i;
35358             }
35359         }
35360         return -1;
35361     },
35362     
35363     /**
35364      * Returns the index for a specified column dataIndex.
35365      * @param {String} dataIndex The column dataIndex
35366      * @return {Number} the index, or -1 if not found
35367      */
35368     
35369     findColumnIndex : function(dataIndex){
35370         for(var i = 0, len = this.config.length; i < len; i++){
35371             if(this.config[i].dataIndex == dataIndex){
35372                 return i;
35373             }
35374         }
35375         return -1;
35376     },
35377     
35378     
35379     moveColumn : function(oldIndex, newIndex){
35380         var c = this.config[oldIndex];
35381         this.config.splice(oldIndex, 1);
35382         this.config.splice(newIndex, 0, c);
35383         this.dataMap = null;
35384         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35385     },
35386
35387     isLocked : function(colIndex){
35388         return this.config[colIndex].locked === true;
35389     },
35390
35391     setLocked : function(colIndex, value, suppressEvent){
35392         if(this.isLocked(colIndex) == value){
35393             return;
35394         }
35395         this.config[colIndex].locked = value;
35396         if(!suppressEvent){
35397             this.fireEvent("columnlockchange", this, colIndex, value);
35398         }
35399     },
35400
35401     getTotalLockedWidth : function(){
35402         var totalWidth = 0;
35403         for(var i = 0; i < this.config.length; i++){
35404             if(this.isLocked(i) && !this.isHidden(i)){
35405                 this.totalWidth += this.getColumnWidth(i);
35406             }
35407         }
35408         return totalWidth;
35409     },
35410
35411     getLockedCount : function(){
35412         for(var i = 0, len = this.config.length; i < len; i++){
35413             if(!this.isLocked(i)){
35414                 return i;
35415             }
35416         }
35417         
35418         return this.config.length;
35419     },
35420
35421     /**
35422      * Returns the number of columns.
35423      * @return {Number}
35424      */
35425     getColumnCount : function(visibleOnly){
35426         if(visibleOnly === true){
35427             var c = 0;
35428             for(var i = 0, len = this.config.length; i < len; i++){
35429                 if(!this.isHidden(i)){
35430                     c++;
35431                 }
35432             }
35433             return c;
35434         }
35435         return this.config.length;
35436     },
35437
35438     /**
35439      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35440      * @param {Function} fn
35441      * @param {Object} scope (optional)
35442      * @return {Array} result
35443      */
35444     getColumnsBy : function(fn, scope){
35445         var r = [];
35446         for(var i = 0, len = this.config.length; i < len; i++){
35447             var c = this.config[i];
35448             if(fn.call(scope||this, c, i) === true){
35449                 r[r.length] = c;
35450             }
35451         }
35452         return r;
35453     },
35454
35455     /**
35456      * Returns true if the specified column is sortable.
35457      * @param {Number} col The column index
35458      * @return {Boolean}
35459      */
35460     isSortable : function(col){
35461         if(typeof this.config[col].sortable == "undefined"){
35462             return this.defaultSortable;
35463         }
35464         return this.config[col].sortable;
35465     },
35466
35467     /**
35468      * Returns the rendering (formatting) function defined for the column.
35469      * @param {Number} col The column index.
35470      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35471      */
35472     getRenderer : function(col){
35473         if(!this.config[col].renderer){
35474             return Roo.grid.ColumnModel.defaultRenderer;
35475         }
35476         return this.config[col].renderer;
35477     },
35478
35479     /**
35480      * Sets the rendering (formatting) function for a column.
35481      * @param {Number} col The column index
35482      * @param {Function} fn The function to use to process the cell's raw data
35483      * to return HTML markup for the grid view. The render function is called with
35484      * the following parameters:<ul>
35485      * <li>Data value.</li>
35486      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35487      * <li>css A CSS style string to apply to the table cell.</li>
35488      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35489      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35490      * <li>Row index</li>
35491      * <li>Column index</li>
35492      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35493      */
35494     setRenderer : function(col, fn){
35495         this.config[col].renderer = fn;
35496     },
35497
35498     /**
35499      * Returns the width for the specified column.
35500      * @param {Number} col The column index
35501      * @param (optional) {String} gridSize bootstrap width size.
35502      * @return {Number}
35503      */
35504     getColumnWidth : function(col, gridSize)
35505         {
35506                 var cfg = this.config[col];
35507                 
35508                 if (typeof(gridSize) == 'undefined') {
35509                         return cfg.width * 1 || this.defaultWidth;
35510                 }
35511                 if (gridSize === false) { // if we set it..
35512                         return cfg.width || false;
35513                 }
35514                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
35515                 
35516                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
35517                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
35518                                 continue;
35519                         }
35520                         return cfg[ sizes[i] ];
35521                 }
35522                 return 1;
35523                 
35524     },
35525
35526     /**
35527      * Sets the width for a column.
35528      * @param {Number} col The column index
35529      * @param {Number} width The new width
35530      */
35531     setColumnWidth : function(col, width, suppressEvent){
35532         this.config[col].width = width;
35533         this.totalWidth = null;
35534         if(!suppressEvent){
35535              this.fireEvent("widthchange", this, col, width);
35536         }
35537     },
35538
35539     /**
35540      * Returns the total width of all columns.
35541      * @param {Boolean} includeHidden True to include hidden column widths
35542      * @return {Number}
35543      */
35544     getTotalWidth : function(includeHidden){
35545         if(!this.totalWidth){
35546             this.totalWidth = 0;
35547             for(var i = 0, len = this.config.length; i < len; i++){
35548                 if(includeHidden || !this.isHidden(i)){
35549                     this.totalWidth += this.getColumnWidth(i);
35550                 }
35551             }
35552         }
35553         return this.totalWidth;
35554     },
35555
35556     /**
35557      * Returns the header for the specified column.
35558      * @param {Number} col The column index
35559      * @return {String}
35560      */
35561     getColumnHeader : function(col){
35562         return this.config[col].header;
35563     },
35564
35565     /**
35566      * Sets the header for a column.
35567      * @param {Number} col The column index
35568      * @param {String} header The new header
35569      */
35570     setColumnHeader : function(col, header){
35571         this.config[col].header = header;
35572         this.fireEvent("headerchange", this, col, header);
35573     },
35574
35575     /**
35576      * Returns the tooltip for the specified column.
35577      * @param {Number} col The column index
35578      * @return {String}
35579      */
35580     getColumnTooltip : function(col){
35581             return this.config[col].tooltip;
35582     },
35583     /**
35584      * Sets the tooltip for a column.
35585      * @param {Number} col The column index
35586      * @param {String} tooltip The new tooltip
35587      */
35588     setColumnTooltip : function(col, tooltip){
35589             this.config[col].tooltip = tooltip;
35590     },
35591
35592     /**
35593      * Returns the dataIndex for the specified column.
35594      * @param {Number} col The column index
35595      * @return {Number}
35596      */
35597     getDataIndex : function(col){
35598         return this.config[col].dataIndex;
35599     },
35600
35601     /**
35602      * Sets the dataIndex for a column.
35603      * @param {Number} col The column index
35604      * @param {Number} dataIndex The new dataIndex
35605      */
35606     setDataIndex : function(col, dataIndex){
35607         this.config[col].dataIndex = dataIndex;
35608     },
35609
35610     
35611     
35612     /**
35613      * Returns true if the cell is editable.
35614      * @param {Number} colIndex The column index
35615      * @param {Number} rowIndex The row index - this is nto actually used..?
35616      * @return {Boolean}
35617      */
35618     isCellEditable : function(colIndex, rowIndex){
35619         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35620     },
35621
35622     /**
35623      * Returns the editor defined for the cell/column.
35624      * return false or null to disable editing.
35625      * @param {Number} colIndex The column index
35626      * @param {Number} rowIndex The row index
35627      * @return {Object}
35628      */
35629     getCellEditor : function(colIndex, rowIndex){
35630         return this.config[colIndex].editor;
35631     },
35632
35633     /**
35634      * Sets if a column is editable.
35635      * @param {Number} col The column index
35636      * @param {Boolean} editable True if the column is editable
35637      */
35638     setEditable : function(col, editable){
35639         this.config[col].editable = editable;
35640     },
35641
35642
35643     /**
35644      * Returns true if the column is hidden.
35645      * @param {Number} colIndex The column index
35646      * @return {Boolean}
35647      */
35648     isHidden : function(colIndex){
35649         return this.config[colIndex].hidden;
35650     },
35651
35652
35653     /**
35654      * Returns true if the column width cannot be changed
35655      */
35656     isFixed : function(colIndex){
35657         return this.config[colIndex].fixed;
35658     },
35659
35660     /**
35661      * Returns true if the column can be resized
35662      * @return {Boolean}
35663      */
35664     isResizable : function(colIndex){
35665         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35666     },
35667     /**
35668      * Sets if a column is hidden.
35669      * @param {Number} colIndex The column index
35670      * @param {Boolean} hidden True if the column is hidden
35671      */
35672     setHidden : function(colIndex, hidden){
35673         this.config[colIndex].hidden = hidden;
35674         this.totalWidth = null;
35675         this.fireEvent("hiddenchange", this, colIndex, hidden);
35676     },
35677
35678     /**
35679      * Sets the editor for a column.
35680      * @param {Number} col The column index
35681      * @param {Object} editor The editor object
35682      */
35683     setEditor : function(col, editor){
35684         this.config[col].editor = editor;
35685     },
35686     /**
35687      * Add a column (experimental...) - defaults to adding to the end..
35688      * @param {Object} config 
35689     */
35690     addColumn : function(c)
35691     {
35692     
35693         var i = this.config.length;
35694         this.config[i] = c;
35695         
35696         if(typeof c.dataIndex == "undefined"){
35697             c.dataIndex = i;
35698         }
35699         if(typeof c.renderer == "string"){
35700             c.renderer = Roo.util.Format[c.renderer];
35701         }
35702         if(typeof c.id == "undefined"){
35703             c.id = Roo.id();
35704         }
35705         if(c.editor && c.editor.xtype){
35706             c.editor  = Roo.factory(c.editor, Roo.grid);
35707         }
35708         if(c.editor && c.editor.isFormField){
35709             c.editor = new Roo.grid.GridEditor(c.editor);
35710         }
35711         this.lookup[c.id] = c;
35712     }
35713     
35714 });
35715
35716 Roo.grid.ColumnModel.defaultRenderer = function(value)
35717 {
35718     if(typeof value == "object") {
35719         return value;
35720     }
35721         if(typeof value == "string" && value.length < 1){
35722             return "&#160;";
35723         }
35724     
35725         return String.format("{0}", value);
35726 };
35727
35728 // Alias for backwards compatibility
35729 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35730 /*
35731  * Based on:
35732  * Ext JS Library 1.1.1
35733  * Copyright(c) 2006-2007, Ext JS, LLC.
35734  *
35735  * Originally Released Under LGPL - original licence link has changed is not relivant.
35736  *
35737  * Fork - LGPL
35738  * <script type="text/javascript">
35739  */
35740
35741 /**
35742  * @class Roo.grid.AbstractSelectionModel
35743  * @extends Roo.util.Observable
35744  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35745  * implemented by descendant classes.  This class should not be directly instantiated.
35746  * @constructor
35747  */
35748 Roo.grid.AbstractSelectionModel = function(){
35749     this.locked = false;
35750     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35751 };
35752
35753 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35754     /** @ignore Called by the grid automatically. Do not call directly. */
35755     init : function(grid){
35756         this.grid = grid;
35757         this.initEvents();
35758     },
35759
35760     /**
35761      * Locks the selections.
35762      */
35763     lock : function(){
35764         this.locked = true;
35765     },
35766
35767     /**
35768      * Unlocks the selections.
35769      */
35770     unlock : function(){
35771         this.locked = false;
35772     },
35773
35774     /**
35775      * Returns true if the selections are locked.
35776      * @return {Boolean}
35777      */
35778     isLocked : function(){
35779         return this.locked;
35780     }
35781 });/*
35782  * Based on:
35783  * Ext JS Library 1.1.1
35784  * Copyright(c) 2006-2007, Ext JS, LLC.
35785  *
35786  * Originally Released Under LGPL - original licence link has changed is not relivant.
35787  *
35788  * Fork - LGPL
35789  * <script type="text/javascript">
35790  */
35791 /**
35792  * @extends Roo.grid.AbstractSelectionModel
35793  * @class Roo.grid.RowSelectionModel
35794  * The default SelectionModel used by {@link Roo.grid.Grid}.
35795  * It supports multiple selections and keyboard selection/navigation. 
35796  * @constructor
35797  * @param {Object} config
35798  */
35799 Roo.grid.RowSelectionModel = function(config){
35800     Roo.apply(this, config);
35801     this.selections = new Roo.util.MixedCollection(false, function(o){
35802         return o.id;
35803     });
35804
35805     this.last = false;
35806     this.lastActive = false;
35807
35808     this.addEvents({
35809         /**
35810         * @event selectionchange
35811         * Fires when the selection changes
35812         * @param {SelectionModel} this
35813         */
35814        "selectionchange" : true,
35815        /**
35816         * @event afterselectionchange
35817         * Fires after the selection changes (eg. by key press or clicking)
35818         * @param {SelectionModel} this
35819         */
35820        "afterselectionchange" : true,
35821        /**
35822         * @event beforerowselect
35823         * Fires when a row is selected being selected, return false to cancel.
35824         * @param {SelectionModel} this
35825         * @param {Number} rowIndex The selected index
35826         * @param {Boolean} keepExisting False if other selections will be cleared
35827         */
35828        "beforerowselect" : true,
35829        /**
35830         * @event rowselect
35831         * Fires when a row is selected.
35832         * @param {SelectionModel} this
35833         * @param {Number} rowIndex The selected index
35834         * @param {Roo.data.Record} r The record
35835         */
35836        "rowselect" : true,
35837        /**
35838         * @event rowdeselect
35839         * Fires when a row is deselected.
35840         * @param {SelectionModel} this
35841         * @param {Number} rowIndex The selected index
35842         */
35843         "rowdeselect" : true
35844     });
35845     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35846     this.locked = false;
35847 };
35848
35849 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35850     /**
35851      * @cfg {Boolean} singleSelect
35852      * True to allow selection of only one row at a time (defaults to false)
35853      */
35854     singleSelect : false,
35855
35856     // private
35857     initEvents : function(){
35858
35859         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35860             this.grid.on("mousedown", this.handleMouseDown, this);
35861         }else{ // allow click to work like normal
35862             this.grid.on("rowclick", this.handleDragableRowClick, this);
35863         }
35864         // bootstrap does not have a view..
35865         var view = this.grid.view ? this.grid.view : this.grid;
35866         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35867             "up" : function(e){
35868                 if(!e.shiftKey){
35869                     this.selectPrevious(e.shiftKey);
35870                 }else if(this.last !== false && this.lastActive !== false){
35871                     var last = this.last;
35872                     this.selectRange(this.last,  this.lastActive-1);
35873                     view.focusRow(this.lastActive);
35874                     if(last !== false){
35875                         this.last = last;
35876                     }
35877                 }else{
35878                     this.selectFirstRow();
35879                 }
35880                 this.fireEvent("afterselectionchange", this);
35881             },
35882             "down" : function(e){
35883                 if(!e.shiftKey){
35884                     this.selectNext(e.shiftKey);
35885                 }else if(this.last !== false && this.lastActive !== false){
35886                     var last = this.last;
35887                     this.selectRange(this.last,  this.lastActive+1);
35888                     view.focusRow(this.lastActive);
35889                     if(last !== false){
35890                         this.last = last;
35891                     }
35892                 }else{
35893                     this.selectFirstRow();
35894                 }
35895                 this.fireEvent("afterselectionchange", this);
35896             },
35897             scope: this
35898         });
35899
35900          
35901         view.on("refresh", this.onRefresh, this);
35902         view.on("rowupdated", this.onRowUpdated, this);
35903         view.on("rowremoved", this.onRemove, this);
35904     },
35905
35906     // private
35907     onRefresh : function(){
35908         var ds = this.grid.ds, i, v = this.grid.view;
35909         var s = this.selections;
35910         s.each(function(r){
35911             if((i = ds.indexOfId(r.id)) != -1){
35912                 v.onRowSelect(i);
35913                 s.add(ds.getAt(i)); // updating the selection relate data
35914             }else{
35915                 s.remove(r);
35916             }
35917         });
35918     },
35919
35920     // private
35921     onRemove : function(v, index, r){
35922         this.selections.remove(r);
35923     },
35924
35925     // private
35926     onRowUpdated : function(v, index, r){
35927         if(this.isSelected(r)){
35928             v.onRowSelect(index);
35929         }
35930     },
35931
35932     /**
35933      * Select records.
35934      * @param {Array} records The records to select
35935      * @param {Boolean} keepExisting (optional) True to keep existing selections
35936      */
35937     selectRecords : function(records, keepExisting){
35938         if(!keepExisting){
35939             this.clearSelections();
35940         }
35941         var ds = this.grid.ds;
35942         for(var i = 0, len = records.length; i < len; i++){
35943             this.selectRow(ds.indexOf(records[i]), true);
35944         }
35945     },
35946
35947     /**
35948      * Gets the number of selected rows.
35949      * @return {Number}
35950      */
35951     getCount : function(){
35952         return this.selections.length;
35953     },
35954
35955     /**
35956      * Selects the first row in the grid.
35957      */
35958     selectFirstRow : function(){
35959         this.selectRow(0);
35960     },
35961
35962     /**
35963      * Select the last row.
35964      * @param {Boolean} keepExisting (optional) True to keep existing selections
35965      */
35966     selectLastRow : function(keepExisting){
35967         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
35968     },
35969
35970     /**
35971      * Selects the row immediately following the last selected row.
35972      * @param {Boolean} keepExisting (optional) True to keep existing selections
35973      */
35974     selectNext : function(keepExisting){
35975         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
35976             this.selectRow(this.last+1, keepExisting);
35977             var view = this.grid.view ? this.grid.view : this.grid;
35978             view.focusRow(this.last);
35979         }
35980     },
35981
35982     /**
35983      * Selects the row that precedes the last selected row.
35984      * @param {Boolean} keepExisting (optional) True to keep existing selections
35985      */
35986     selectPrevious : function(keepExisting){
35987         if(this.last){
35988             this.selectRow(this.last-1, keepExisting);
35989             var view = this.grid.view ? this.grid.view : this.grid;
35990             view.focusRow(this.last);
35991         }
35992     },
35993
35994     /**
35995      * Returns the selected records
35996      * @return {Array} Array of selected records
35997      */
35998     getSelections : function(){
35999         return [].concat(this.selections.items);
36000     },
36001
36002     /**
36003      * Returns the first selected record.
36004      * @return {Record}
36005      */
36006     getSelected : function(){
36007         return this.selections.itemAt(0);
36008     },
36009
36010
36011     /**
36012      * Clears all selections.
36013      */
36014     clearSelections : function(fast){
36015         if(this.locked) {
36016             return;
36017         }
36018         if(fast !== true){
36019             var ds = this.grid.ds;
36020             var s = this.selections;
36021             s.each(function(r){
36022                 this.deselectRow(ds.indexOfId(r.id));
36023             }, this);
36024             s.clear();
36025         }else{
36026             this.selections.clear();
36027         }
36028         this.last = false;
36029     },
36030
36031
36032     /**
36033      * Selects all rows.
36034      */
36035     selectAll : function(){
36036         if(this.locked) {
36037             return;
36038         }
36039         this.selections.clear();
36040         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
36041             this.selectRow(i, true);
36042         }
36043     },
36044
36045     /**
36046      * Returns True if there is a selection.
36047      * @return {Boolean}
36048      */
36049     hasSelection : function(){
36050         return this.selections.length > 0;
36051     },
36052
36053     /**
36054      * Returns True if the specified row is selected.
36055      * @param {Number/Record} record The record or index of the record to check
36056      * @return {Boolean}
36057      */
36058     isSelected : function(index){
36059         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
36060         return (r && this.selections.key(r.id) ? true : false);
36061     },
36062
36063     /**
36064      * Returns True if the specified record id is selected.
36065      * @param {String} id The id of record to check
36066      * @return {Boolean}
36067      */
36068     isIdSelected : function(id){
36069         return (this.selections.key(id) ? true : false);
36070     },
36071
36072     // private
36073     handleMouseDown : function(e, t)
36074     {
36075         var view = this.grid.view ? this.grid.view : this.grid;
36076         var rowIndex;
36077         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36078             return;
36079         };
36080         if(e.shiftKey && this.last !== false){
36081             var last = this.last;
36082             this.selectRange(last, rowIndex, e.ctrlKey);
36083             this.last = last; // reset the last
36084             view.focusRow(rowIndex);
36085         }else{
36086             var isSelected = this.isSelected(rowIndex);
36087             if(e.button !== 0 && isSelected){
36088                 view.focusRow(rowIndex);
36089             }else if(e.ctrlKey && isSelected){
36090                 this.deselectRow(rowIndex);
36091             }else if(!isSelected){
36092                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36093                 view.focusRow(rowIndex);
36094             }
36095         }
36096         this.fireEvent("afterselectionchange", this);
36097     },
36098     // private
36099     handleDragableRowClick :  function(grid, rowIndex, e) 
36100     {
36101         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36102             this.selectRow(rowIndex, false);
36103             var view = this.grid.view ? this.grid.view : this.grid;
36104             view.focusRow(rowIndex);
36105              this.fireEvent("afterselectionchange", this);
36106         }
36107     },
36108     
36109     /**
36110      * Selects multiple rows.
36111      * @param {Array} rows Array of the indexes of the row to select
36112      * @param {Boolean} keepExisting (optional) True to keep existing selections
36113      */
36114     selectRows : function(rows, keepExisting){
36115         if(!keepExisting){
36116             this.clearSelections();
36117         }
36118         for(var i = 0, len = rows.length; i < len; i++){
36119             this.selectRow(rows[i], true);
36120         }
36121     },
36122
36123     /**
36124      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36125      * @param {Number} startRow The index of the first row in the range
36126      * @param {Number} endRow The index of the last row in the range
36127      * @param {Boolean} keepExisting (optional) True to retain existing selections
36128      */
36129     selectRange : function(startRow, endRow, keepExisting){
36130         if(this.locked) {
36131             return;
36132         }
36133         if(!keepExisting){
36134             this.clearSelections();
36135         }
36136         if(startRow <= endRow){
36137             for(var i = startRow; i <= endRow; i++){
36138                 this.selectRow(i, true);
36139             }
36140         }else{
36141             for(var i = startRow; i >= endRow; i--){
36142                 this.selectRow(i, true);
36143             }
36144         }
36145     },
36146
36147     /**
36148      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36149      * @param {Number} startRow The index of the first row in the range
36150      * @param {Number} endRow The index of the last row in the range
36151      */
36152     deselectRange : function(startRow, endRow, preventViewNotify){
36153         if(this.locked) {
36154             return;
36155         }
36156         for(var i = startRow; i <= endRow; i++){
36157             this.deselectRow(i, preventViewNotify);
36158         }
36159     },
36160
36161     /**
36162      * Selects a row.
36163      * @param {Number} row The index of the row to select
36164      * @param {Boolean} keepExisting (optional) True to keep existing selections
36165      */
36166     selectRow : function(index, keepExisting, preventViewNotify){
36167         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
36168             return;
36169         }
36170         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36171             if(!keepExisting || this.singleSelect){
36172                 this.clearSelections();
36173             }
36174             var r = this.grid.ds.getAt(index);
36175             this.selections.add(r);
36176             this.last = this.lastActive = index;
36177             if(!preventViewNotify){
36178                 var view = this.grid.view ? this.grid.view : this.grid;
36179                 view.onRowSelect(index);
36180             }
36181             this.fireEvent("rowselect", this, index, r);
36182             this.fireEvent("selectionchange", this);
36183         }
36184     },
36185
36186     /**
36187      * Deselects a row.
36188      * @param {Number} row The index of the row to deselect
36189      */
36190     deselectRow : function(index, preventViewNotify){
36191         if(this.locked) {
36192             return;
36193         }
36194         if(this.last == index){
36195             this.last = false;
36196         }
36197         if(this.lastActive == index){
36198             this.lastActive = false;
36199         }
36200         var r = this.grid.ds.getAt(index);
36201         this.selections.remove(r);
36202         if(!preventViewNotify){
36203             var view = this.grid.view ? this.grid.view : this.grid;
36204             view.onRowDeselect(index);
36205         }
36206         this.fireEvent("rowdeselect", this, index);
36207         this.fireEvent("selectionchange", this);
36208     },
36209
36210     // private
36211     restoreLast : function(){
36212         if(this._last){
36213             this.last = this._last;
36214         }
36215     },
36216
36217     // private
36218     acceptsNav : function(row, col, cm){
36219         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36220     },
36221
36222     // private
36223     onEditorKey : function(field, e){
36224         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36225         if(k == e.TAB){
36226             e.stopEvent();
36227             ed.completeEdit();
36228             if(e.shiftKey){
36229                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36230             }else{
36231                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36232             }
36233         }else if(k == e.ENTER && !e.ctrlKey){
36234             e.stopEvent();
36235             ed.completeEdit();
36236             if(e.shiftKey){
36237                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36238             }else{
36239                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36240             }
36241         }else if(k == e.ESC){
36242             ed.cancelEdit();
36243         }
36244         if(newCell){
36245             g.startEditing(newCell[0], newCell[1]);
36246         }
36247     }
36248 });/*
36249  * Based on:
36250  * Ext JS Library 1.1.1
36251  * Copyright(c) 2006-2007, Ext JS, LLC.
36252  *
36253  * Originally Released Under LGPL - original licence link has changed is not relivant.
36254  *
36255  * Fork - LGPL
36256  * <script type="text/javascript">
36257  */
36258 /**
36259  * @class Roo.grid.CellSelectionModel
36260  * @extends Roo.grid.AbstractSelectionModel
36261  * This class provides the basic implementation for cell selection in a grid.
36262  * @constructor
36263  * @param {Object} config The object containing the configuration of this model.
36264  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36265  */
36266 Roo.grid.CellSelectionModel = function(config){
36267     Roo.apply(this, config);
36268
36269     this.selection = null;
36270
36271     this.addEvents({
36272         /**
36273              * @event beforerowselect
36274              * Fires before a cell is selected.
36275              * @param {SelectionModel} this
36276              * @param {Number} rowIndex The selected row index
36277              * @param {Number} colIndex The selected cell index
36278              */
36279             "beforecellselect" : true,
36280         /**
36281              * @event cellselect
36282              * Fires when a cell is selected.
36283              * @param {SelectionModel} this
36284              * @param {Number} rowIndex The selected row index
36285              * @param {Number} colIndex The selected cell index
36286              */
36287             "cellselect" : true,
36288         /**
36289              * @event selectionchange
36290              * Fires when the active selection changes.
36291              * @param {SelectionModel} this
36292              * @param {Object} selection null for no selection or an object (o) with two properties
36293                 <ul>
36294                 <li>o.record: the record object for the row the selection is in</li>
36295                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36296                 </ul>
36297              */
36298             "selectionchange" : true,
36299         /**
36300              * @event tabend
36301              * Fires when the tab (or enter) was pressed on the last editable cell
36302              * You can use this to trigger add new row.
36303              * @param {SelectionModel} this
36304              */
36305             "tabend" : true,
36306          /**
36307              * @event beforeeditnext
36308              * Fires before the next editable sell is made active
36309              * You can use this to skip to another cell or fire the tabend
36310              *    if you set cell to false
36311              * @param {Object} eventdata object : { cell : [ row, col ] } 
36312              */
36313             "beforeeditnext" : true
36314     });
36315     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36316 };
36317
36318 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36319     
36320     enter_is_tab: false,
36321
36322     /** @ignore */
36323     initEvents : function(){
36324         this.grid.on("mousedown", this.handleMouseDown, this);
36325         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36326         var view = this.grid.view;
36327         view.on("refresh", this.onViewChange, this);
36328         view.on("rowupdated", this.onRowUpdated, this);
36329         view.on("beforerowremoved", this.clearSelections, this);
36330         view.on("beforerowsinserted", this.clearSelections, this);
36331         if(this.grid.isEditor){
36332             this.grid.on("beforeedit", this.beforeEdit,  this);
36333         }
36334     },
36335
36336         //private
36337     beforeEdit : function(e){
36338         this.select(e.row, e.column, false, true, e.record);
36339     },
36340
36341         //private
36342     onRowUpdated : function(v, index, r){
36343         if(this.selection && this.selection.record == r){
36344             v.onCellSelect(index, this.selection.cell[1]);
36345         }
36346     },
36347
36348         //private
36349     onViewChange : function(){
36350         this.clearSelections(true);
36351     },
36352
36353         /**
36354          * Returns the currently selected cell,.
36355          * @return {Array} The selected cell (row, column) or null if none selected.
36356          */
36357     getSelectedCell : function(){
36358         return this.selection ? this.selection.cell : null;
36359     },
36360
36361     /**
36362      * Clears all selections.
36363      * @param {Boolean} true to prevent the gridview from being notified about the change.
36364      */
36365     clearSelections : function(preventNotify){
36366         var s = this.selection;
36367         if(s){
36368             if(preventNotify !== true){
36369                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36370             }
36371             this.selection = null;
36372             this.fireEvent("selectionchange", this, null);
36373         }
36374     },
36375
36376     /**
36377      * Returns true if there is a selection.
36378      * @return {Boolean}
36379      */
36380     hasSelection : function(){
36381         return this.selection ? true : false;
36382     },
36383
36384     /** @ignore */
36385     handleMouseDown : function(e, t){
36386         var v = this.grid.getView();
36387         if(this.isLocked()){
36388             return;
36389         };
36390         var row = v.findRowIndex(t);
36391         var cell = v.findCellIndex(t);
36392         if(row !== false && cell !== false){
36393             this.select(row, cell);
36394         }
36395     },
36396
36397     /**
36398      * Selects a cell.
36399      * @param {Number} rowIndex
36400      * @param {Number} collIndex
36401      */
36402     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36403         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36404             this.clearSelections();
36405             r = r || this.grid.dataSource.getAt(rowIndex);
36406             this.selection = {
36407                 record : r,
36408                 cell : [rowIndex, colIndex]
36409             };
36410             if(!preventViewNotify){
36411                 var v = this.grid.getView();
36412                 v.onCellSelect(rowIndex, colIndex);
36413                 if(preventFocus !== true){
36414                     v.focusCell(rowIndex, colIndex);
36415                 }
36416             }
36417             this.fireEvent("cellselect", this, rowIndex, colIndex);
36418             this.fireEvent("selectionchange", this, this.selection);
36419         }
36420     },
36421
36422         //private
36423     isSelectable : function(rowIndex, colIndex, cm){
36424         return !cm.isHidden(colIndex);
36425     },
36426
36427     /** @ignore */
36428     handleKeyDown : function(e){
36429         //Roo.log('Cell Sel Model handleKeyDown');
36430         if(!e.isNavKeyPress()){
36431             return;
36432         }
36433         var g = this.grid, s = this.selection;
36434         if(!s){
36435             e.stopEvent();
36436             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36437             if(cell){
36438                 this.select(cell[0], cell[1]);
36439             }
36440             return;
36441         }
36442         var sm = this;
36443         var walk = function(row, col, step){
36444             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36445         };
36446         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36447         var newCell;
36448
36449       
36450
36451         switch(k){
36452             case e.TAB:
36453                 // handled by onEditorKey
36454                 if (g.isEditor && g.editing) {
36455                     return;
36456                 }
36457                 if(e.shiftKey) {
36458                     newCell = walk(r, c-1, -1);
36459                 } else {
36460                     newCell = walk(r, c+1, 1);
36461                 }
36462                 break;
36463             
36464             case e.DOWN:
36465                newCell = walk(r+1, c, 1);
36466                 break;
36467             
36468             case e.UP:
36469                 newCell = walk(r-1, c, -1);
36470                 break;
36471             
36472             case e.RIGHT:
36473                 newCell = walk(r, c+1, 1);
36474                 break;
36475             
36476             case e.LEFT:
36477                 newCell = walk(r, c-1, -1);
36478                 break;
36479             
36480             case e.ENTER:
36481                 
36482                 if(g.isEditor && !g.editing){
36483                    g.startEditing(r, c);
36484                    e.stopEvent();
36485                    return;
36486                 }
36487                 
36488                 
36489              break;
36490         };
36491         if(newCell){
36492             this.select(newCell[0], newCell[1]);
36493             e.stopEvent();
36494             
36495         }
36496     },
36497
36498     acceptsNav : function(row, col, cm){
36499         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36500     },
36501     /**
36502      * Selects a cell.
36503      * @param {Number} field (not used) - as it's normally used as a listener
36504      * @param {Number} e - event - fake it by using
36505      *
36506      * var e = Roo.EventObjectImpl.prototype;
36507      * e.keyCode = e.TAB
36508      *
36509      * 
36510      */
36511     onEditorKey : function(field, e){
36512         
36513         var k = e.getKey(),
36514             newCell,
36515             g = this.grid,
36516             ed = g.activeEditor,
36517             forward = false;
36518         ///Roo.log('onEditorKey' + k);
36519         
36520         
36521         if (this.enter_is_tab && k == e.ENTER) {
36522             k = e.TAB;
36523         }
36524         
36525         if(k == e.TAB){
36526             if(e.shiftKey){
36527                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36528             }else{
36529                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36530                 forward = true;
36531             }
36532             
36533             e.stopEvent();
36534             
36535         } else if(k == e.ENTER &&  !e.ctrlKey){
36536             ed.completeEdit();
36537             e.stopEvent();
36538             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36539         
36540                 } else if(k == e.ESC){
36541             ed.cancelEdit();
36542         }
36543                 
36544         if (newCell) {
36545             var ecall = { cell : newCell, forward : forward };
36546             this.fireEvent('beforeeditnext', ecall );
36547             newCell = ecall.cell;
36548                         forward = ecall.forward;
36549         }
36550                 
36551         if(newCell){
36552             //Roo.log('next cell after edit');
36553             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36554         } else if (forward) {
36555             // tabbed past last
36556             this.fireEvent.defer(100, this, ['tabend',this]);
36557         }
36558     }
36559 });/*
36560  * Based on:
36561  * Ext JS Library 1.1.1
36562  * Copyright(c) 2006-2007, Ext JS, LLC.
36563  *
36564  * Originally Released Under LGPL - original licence link has changed is not relivant.
36565  *
36566  * Fork - LGPL
36567  * <script type="text/javascript">
36568  */
36569  
36570 /**
36571  * @class Roo.grid.EditorGrid
36572  * @extends Roo.grid.Grid
36573  * Class for creating and editable grid.
36574  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36575  * The container MUST have some type of size defined for the grid to fill. The container will be 
36576  * automatically set to position relative if it isn't already.
36577  * @param {Object} dataSource The data model to bind to
36578  * @param {Object} colModel The column model with info about this grid's columns
36579  */
36580 Roo.grid.EditorGrid = function(container, config){
36581     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36582     this.getGridEl().addClass("xedit-grid");
36583
36584     if(!this.selModel){
36585         this.selModel = new Roo.grid.CellSelectionModel();
36586     }
36587
36588     this.activeEditor = null;
36589
36590         this.addEvents({
36591             /**
36592              * @event beforeedit
36593              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36594              * <ul style="padding:5px;padding-left:16px;">
36595              * <li>grid - This grid</li>
36596              * <li>record - The record being edited</li>
36597              * <li>field - The field name being edited</li>
36598              * <li>value - The value for the field being edited.</li>
36599              * <li>row - The grid row index</li>
36600              * <li>column - The grid column index</li>
36601              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36602              * </ul>
36603              * @param {Object} e An edit event (see above for description)
36604              */
36605             "beforeedit" : true,
36606             /**
36607              * @event afteredit
36608              * Fires after a cell is edited. <br />
36609              * <ul style="padding:5px;padding-left:16px;">
36610              * <li>grid - This grid</li>
36611              * <li>record - The record being edited</li>
36612              * <li>field - The field name being edited</li>
36613              * <li>value - The value being set</li>
36614              * <li>originalValue - The original value for the field, before the edit.</li>
36615              * <li>row - The grid row index</li>
36616              * <li>column - The grid column index</li>
36617              * </ul>
36618              * @param {Object} e An edit event (see above for description)
36619              */
36620             "afteredit" : true,
36621             /**
36622              * @event validateedit
36623              * Fires after a cell is edited, but before the value is set in the record. 
36624          * You can use this to modify the value being set in the field, Return false
36625              * to cancel the change. The edit event object has the following properties <br />
36626              * <ul style="padding:5px;padding-left:16px;">
36627          * <li>editor - This editor</li>
36628              * <li>grid - This grid</li>
36629              * <li>record - The record being edited</li>
36630              * <li>field - The field name being edited</li>
36631              * <li>value - The value being set</li>
36632              * <li>originalValue - The original value for the field, before the edit.</li>
36633              * <li>row - The grid row index</li>
36634              * <li>column - The grid column index</li>
36635              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36636              * </ul>
36637              * @param {Object} e An edit event (see above for description)
36638              */
36639             "validateedit" : true
36640         });
36641     this.on("bodyscroll", this.stopEditing,  this);
36642     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36643 };
36644
36645 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36646     /**
36647      * @cfg {Number} clicksToEdit
36648      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36649      */
36650     clicksToEdit: 2,
36651
36652     // private
36653     isEditor : true,
36654     // private
36655     trackMouseOver: false, // causes very odd FF errors
36656
36657     onCellDblClick : function(g, row, col){
36658         this.startEditing(row, col);
36659     },
36660
36661     onEditComplete : function(ed, value, startValue){
36662         this.editing = false;
36663         this.activeEditor = null;
36664         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36665         var r = ed.record;
36666         var field = this.colModel.getDataIndex(ed.col);
36667         var e = {
36668             grid: this,
36669             record: r,
36670             field: field,
36671             originalValue: startValue,
36672             value: value,
36673             row: ed.row,
36674             column: ed.col,
36675             cancel:false,
36676             editor: ed
36677         };
36678         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36679         cell.show();
36680           
36681         if(String(value) !== String(startValue)){
36682             
36683             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36684                 r.set(field, e.value);
36685                 // if we are dealing with a combo box..
36686                 // then we also set the 'name' colum to be the displayField
36687                 if (ed.field.displayField && ed.field.name) {
36688                     r.set(ed.field.name, ed.field.el.dom.value);
36689                 }
36690                 
36691                 delete e.cancel; //?? why!!!
36692                 this.fireEvent("afteredit", e);
36693             }
36694         } else {
36695             this.fireEvent("afteredit", e); // always fire it!
36696         }
36697         this.view.focusCell(ed.row, ed.col);
36698     },
36699
36700     /**
36701      * Starts editing the specified for the specified row/column
36702      * @param {Number} rowIndex
36703      * @param {Number} colIndex
36704      */
36705     startEditing : function(row, col){
36706         this.stopEditing();
36707         if(this.colModel.isCellEditable(col, row)){
36708             this.view.ensureVisible(row, col, true);
36709           
36710             var r = this.dataSource.getAt(row);
36711             var field = this.colModel.getDataIndex(col);
36712             var cell = Roo.get(this.view.getCell(row,col));
36713             var e = {
36714                 grid: this,
36715                 record: r,
36716                 field: field,
36717                 value: r.data[field],
36718                 row: row,
36719                 column: col,
36720                 cancel:false 
36721             };
36722             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36723                 this.editing = true;
36724                 var ed = this.colModel.getCellEditor(col, row);
36725                 
36726                 if (!ed) {
36727                     return;
36728                 }
36729                 if(!ed.rendered){
36730                     ed.render(ed.parentEl || document.body);
36731                 }
36732                 ed.field.reset();
36733                
36734                 cell.hide();
36735                 
36736                 (function(){ // complex but required for focus issues in safari, ie and opera
36737                     ed.row = row;
36738                     ed.col = col;
36739                     ed.record = r;
36740                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36741                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36742                     this.activeEditor = ed;
36743                     var v = r.data[field];
36744                     ed.startEdit(this.view.getCell(row, col), v);
36745                     // combo's with 'displayField and name set
36746                     if (ed.field.displayField && ed.field.name) {
36747                         ed.field.el.dom.value = r.data[ed.field.name];
36748                     }
36749                     
36750                     
36751                 }).defer(50, this);
36752             }
36753         }
36754     },
36755         
36756     /**
36757      * Stops any active editing
36758      */
36759     stopEditing : function(){
36760         if(this.activeEditor){
36761             this.activeEditor.completeEdit();
36762         }
36763         this.activeEditor = null;
36764     },
36765         
36766          /**
36767      * Called to get grid's drag proxy text, by default returns this.ddText.
36768      * @return {String}
36769      */
36770     getDragDropText : function(){
36771         var count = this.selModel.getSelectedCell() ? 1 : 0;
36772         return String.format(this.ddText, count, count == 1 ? '' : 's');
36773     }
36774         
36775 });/*
36776  * Based on:
36777  * Ext JS Library 1.1.1
36778  * Copyright(c) 2006-2007, Ext JS, LLC.
36779  *
36780  * Originally Released Under LGPL - original licence link has changed is not relivant.
36781  *
36782  * Fork - LGPL
36783  * <script type="text/javascript">
36784  */
36785
36786 // private - not really -- you end up using it !
36787 // This is a support class used internally by the Grid components
36788
36789 /**
36790  * @class Roo.grid.GridEditor
36791  * @extends Roo.Editor
36792  * Class for creating and editable grid elements.
36793  * @param {Object} config any settings (must include field)
36794  */
36795 Roo.grid.GridEditor = function(field, config){
36796     if (!config && field.field) {
36797         config = field;
36798         field = Roo.factory(config.field, Roo.form);
36799     }
36800     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36801     field.monitorTab = false;
36802 };
36803
36804 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36805     
36806     /**
36807      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36808      */
36809     
36810     alignment: "tl-tl",
36811     autoSize: "width",
36812     hideEl : false,
36813     cls: "x-small-editor x-grid-editor",
36814     shim:false,
36815     shadow:"frame"
36816 });/*
36817  * Based on:
36818  * Ext JS Library 1.1.1
36819  * Copyright(c) 2006-2007, Ext JS, LLC.
36820  *
36821  * Originally Released Under LGPL - original licence link has changed is not relivant.
36822  *
36823  * Fork - LGPL
36824  * <script type="text/javascript">
36825  */
36826   
36827
36828   
36829 Roo.grid.PropertyRecord = Roo.data.Record.create([
36830     {name:'name',type:'string'},  'value'
36831 ]);
36832
36833
36834 Roo.grid.PropertyStore = function(grid, source){
36835     this.grid = grid;
36836     this.store = new Roo.data.Store({
36837         recordType : Roo.grid.PropertyRecord
36838     });
36839     this.store.on('update', this.onUpdate,  this);
36840     if(source){
36841         this.setSource(source);
36842     }
36843     Roo.grid.PropertyStore.superclass.constructor.call(this);
36844 };
36845
36846
36847
36848 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36849     setSource : function(o){
36850         this.source = o;
36851         this.store.removeAll();
36852         var data = [];
36853         for(var k in o){
36854             if(this.isEditableValue(o[k])){
36855                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36856             }
36857         }
36858         this.store.loadRecords({records: data}, {}, true);
36859     },
36860
36861     onUpdate : function(ds, record, type){
36862         if(type == Roo.data.Record.EDIT){
36863             var v = record.data['value'];
36864             var oldValue = record.modified['value'];
36865             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36866                 this.source[record.id] = v;
36867                 record.commit();
36868                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36869             }else{
36870                 record.reject();
36871             }
36872         }
36873     },
36874
36875     getProperty : function(row){
36876        return this.store.getAt(row);
36877     },
36878
36879     isEditableValue: function(val){
36880         if(val && val instanceof Date){
36881             return true;
36882         }else if(typeof val == 'object' || typeof val == 'function'){
36883             return false;
36884         }
36885         return true;
36886     },
36887
36888     setValue : function(prop, value){
36889         this.source[prop] = value;
36890         this.store.getById(prop).set('value', value);
36891     },
36892
36893     getSource : function(){
36894         return this.source;
36895     }
36896 });
36897
36898 Roo.grid.PropertyColumnModel = function(grid, store){
36899     this.grid = grid;
36900     var g = Roo.grid;
36901     g.PropertyColumnModel.superclass.constructor.call(this, [
36902         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36903         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36904     ]);
36905     this.store = store;
36906     this.bselect = Roo.DomHelper.append(document.body, {
36907         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36908             {tag: 'option', value: 'true', html: 'true'},
36909             {tag: 'option', value: 'false', html: 'false'}
36910         ]
36911     });
36912     Roo.id(this.bselect);
36913     var f = Roo.form;
36914     this.editors = {
36915         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36916         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36917         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36918         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36919         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36920     };
36921     this.renderCellDelegate = this.renderCell.createDelegate(this);
36922     this.renderPropDelegate = this.renderProp.createDelegate(this);
36923 };
36924
36925 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36926     
36927     
36928     nameText : 'Name',
36929     valueText : 'Value',
36930     
36931     dateFormat : 'm/j/Y',
36932     
36933     
36934     renderDate : function(dateVal){
36935         return dateVal.dateFormat(this.dateFormat);
36936     },
36937
36938     renderBool : function(bVal){
36939         return bVal ? 'true' : 'false';
36940     },
36941
36942     isCellEditable : function(colIndex, rowIndex){
36943         return colIndex == 1;
36944     },
36945
36946     getRenderer : function(col){
36947         return col == 1 ?
36948             this.renderCellDelegate : this.renderPropDelegate;
36949     },
36950
36951     renderProp : function(v){
36952         return this.getPropertyName(v);
36953     },
36954
36955     renderCell : function(val){
36956         var rv = val;
36957         if(val instanceof Date){
36958             rv = this.renderDate(val);
36959         }else if(typeof val == 'boolean'){
36960             rv = this.renderBool(val);
36961         }
36962         return Roo.util.Format.htmlEncode(rv);
36963     },
36964
36965     getPropertyName : function(name){
36966         var pn = this.grid.propertyNames;
36967         return pn && pn[name] ? pn[name] : name;
36968     },
36969
36970     getCellEditor : function(colIndex, rowIndex){
36971         var p = this.store.getProperty(rowIndex);
36972         var n = p.data['name'], val = p.data['value'];
36973         
36974         if(typeof(this.grid.customEditors[n]) == 'string'){
36975             return this.editors[this.grid.customEditors[n]];
36976         }
36977         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36978             return this.grid.customEditors[n];
36979         }
36980         if(val instanceof Date){
36981             return this.editors['date'];
36982         }else if(typeof val == 'number'){
36983             return this.editors['number'];
36984         }else if(typeof val == 'boolean'){
36985             return this.editors['boolean'];
36986         }else{
36987             return this.editors['string'];
36988         }
36989     }
36990 });
36991
36992 /**
36993  * @class Roo.grid.PropertyGrid
36994  * @extends Roo.grid.EditorGrid
36995  * This class represents the  interface of a component based property grid control.
36996  * <br><br>Usage:<pre><code>
36997  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36998       
36999  });
37000  // set any options
37001  grid.render();
37002  * </code></pre>
37003   
37004  * @constructor
37005  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37006  * The container MUST have some type of size defined for the grid to fill. The container will be
37007  * automatically set to position relative if it isn't already.
37008  * @param {Object} config A config object that sets properties on this grid.
37009  */
37010 Roo.grid.PropertyGrid = function(container, config){
37011     config = config || {};
37012     var store = new Roo.grid.PropertyStore(this);
37013     this.store = store;
37014     var cm = new Roo.grid.PropertyColumnModel(this, store);
37015     store.store.sort('name', 'ASC');
37016     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37017         ds: store.store,
37018         cm: cm,
37019         enableColLock:false,
37020         enableColumnMove:false,
37021         stripeRows:false,
37022         trackMouseOver: false,
37023         clicksToEdit:1
37024     }, config));
37025     this.getGridEl().addClass('x-props-grid');
37026     this.lastEditRow = null;
37027     this.on('columnresize', this.onColumnResize, this);
37028     this.addEvents({
37029          /**
37030              * @event beforepropertychange
37031              * Fires before a property changes (return false to stop?)
37032              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37033              * @param {String} id Record Id
37034              * @param {String} newval New Value
37035          * @param {String} oldval Old Value
37036              */
37037         "beforepropertychange": true,
37038         /**
37039              * @event propertychange
37040              * Fires after a property changes
37041              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37042              * @param {String} id Record Id
37043              * @param {String} newval New Value
37044          * @param {String} oldval Old Value
37045              */
37046         "propertychange": true
37047     });
37048     this.customEditors = this.customEditors || {};
37049 };
37050 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37051     
37052      /**
37053      * @cfg {Object} customEditors map of colnames=> custom editors.
37054      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37055      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37056      * false disables editing of the field.
37057          */
37058     
37059       /**
37060      * @cfg {Object} propertyNames map of property Names to their displayed value
37061          */
37062     
37063     render : function(){
37064         Roo.grid.PropertyGrid.superclass.render.call(this);
37065         this.autoSize.defer(100, this);
37066     },
37067
37068     autoSize : function(){
37069         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37070         if(this.view){
37071             this.view.fitColumns();
37072         }
37073     },
37074
37075     onColumnResize : function(){
37076         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37077         this.autoSize();
37078     },
37079     /**
37080      * Sets the data for the Grid
37081      * accepts a Key => Value object of all the elements avaiable.
37082      * @param {Object} data  to appear in grid.
37083      */
37084     setSource : function(source){
37085         this.store.setSource(source);
37086         //this.autoSize();
37087     },
37088     /**
37089      * Gets all the data from the grid.
37090      * @return {Object} data  data stored in grid
37091      */
37092     getSource : function(){
37093         return this.store.getSource();
37094     }
37095 });/*
37096   
37097  * Licence LGPL
37098  
37099  */
37100  
37101 /**
37102  * @class Roo.grid.Calendar
37103  * @extends Roo.util.Grid
37104  * This class extends the Grid to provide a calendar widget
37105  * <br><br>Usage:<pre><code>
37106  var grid = new Roo.grid.Calendar("my-container-id", {
37107      ds: myDataStore,
37108      cm: myColModel,
37109      selModel: mySelectionModel,
37110      autoSizeColumns: true,
37111      monitorWindowResize: false,
37112      trackMouseOver: true
37113      eventstore : real data store..
37114  });
37115  // set any options
37116  grid.render();
37117   
37118   * @constructor
37119  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37120  * The container MUST have some type of size defined for the grid to fill. The container will be
37121  * automatically set to position relative if it isn't already.
37122  * @param {Object} config A config object that sets properties on this grid.
37123  */
37124 Roo.grid.Calendar = function(container, config){
37125         // initialize the container
37126         this.container = Roo.get(container);
37127         this.container.update("");
37128         this.container.setStyle("overflow", "hidden");
37129     this.container.addClass('x-grid-container');
37130
37131     this.id = this.container.id;
37132
37133     Roo.apply(this, config);
37134     // check and correct shorthanded configs
37135     
37136     var rows = [];
37137     var d =1;
37138     for (var r = 0;r < 6;r++) {
37139         
37140         rows[r]=[];
37141         for (var c =0;c < 7;c++) {
37142             rows[r][c]= '';
37143         }
37144     }
37145     if (this.eventStore) {
37146         this.eventStore= Roo.factory(this.eventStore, Roo.data);
37147         this.eventStore.on('load',this.onLoad, this);
37148         this.eventStore.on('beforeload',this.clearEvents, this);
37149          
37150     }
37151     
37152     this.dataSource = new Roo.data.Store({
37153             proxy: new Roo.data.MemoryProxy(rows),
37154             reader: new Roo.data.ArrayReader({}, [
37155                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37156     });
37157
37158     this.dataSource.load();
37159     this.ds = this.dataSource;
37160     this.ds.xmodule = this.xmodule || false;
37161     
37162     
37163     var cellRender = function(v,x,r)
37164     {
37165         return String.format(
37166             '<div class="fc-day  fc-widget-content"><div>' +
37167                 '<div class="fc-event-container"></div>' +
37168                 '<div class="fc-day-number">{0}</div>'+
37169                 
37170                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37171             '</div></div>', v);
37172     
37173     }
37174     
37175     
37176     this.colModel = new Roo.grid.ColumnModel( [
37177         {
37178             xtype: 'ColumnModel',
37179             xns: Roo.grid,
37180             dataIndex : 'weekday0',
37181             header : 'Sunday',
37182             renderer : cellRender
37183         },
37184         {
37185             xtype: 'ColumnModel',
37186             xns: Roo.grid,
37187             dataIndex : 'weekday1',
37188             header : 'Monday',
37189             renderer : cellRender
37190         },
37191         {
37192             xtype: 'ColumnModel',
37193             xns: Roo.grid,
37194             dataIndex : 'weekday2',
37195             header : 'Tuesday',
37196             renderer : cellRender
37197         },
37198         {
37199             xtype: 'ColumnModel',
37200             xns: Roo.grid,
37201             dataIndex : 'weekday3',
37202             header : 'Wednesday',
37203             renderer : cellRender
37204         },
37205         {
37206             xtype: 'ColumnModel',
37207             xns: Roo.grid,
37208             dataIndex : 'weekday4',
37209             header : 'Thursday',
37210             renderer : cellRender
37211         },
37212         {
37213             xtype: 'ColumnModel',
37214             xns: Roo.grid,
37215             dataIndex : 'weekday5',
37216             header : 'Friday',
37217             renderer : cellRender
37218         },
37219         {
37220             xtype: 'ColumnModel',
37221             xns: Roo.grid,
37222             dataIndex : 'weekday6',
37223             header : 'Saturday',
37224             renderer : cellRender
37225         }
37226     ]);
37227     this.cm = this.colModel;
37228     this.cm.xmodule = this.xmodule || false;
37229  
37230         
37231           
37232     //this.selModel = new Roo.grid.CellSelectionModel();
37233     //this.sm = this.selModel;
37234     //this.selModel.init(this);
37235     
37236     
37237     if(this.width){
37238         this.container.setWidth(this.width);
37239     }
37240
37241     if(this.height){
37242         this.container.setHeight(this.height);
37243     }
37244     /** @private */
37245         this.addEvents({
37246         // raw events
37247         /**
37248          * @event click
37249          * The raw click event for the entire grid.
37250          * @param {Roo.EventObject} e
37251          */
37252         "click" : true,
37253         /**
37254          * @event dblclick
37255          * The raw dblclick event for the entire grid.
37256          * @param {Roo.EventObject} e
37257          */
37258         "dblclick" : true,
37259         /**
37260          * @event contextmenu
37261          * The raw contextmenu event for the entire grid.
37262          * @param {Roo.EventObject} e
37263          */
37264         "contextmenu" : true,
37265         /**
37266          * @event mousedown
37267          * The raw mousedown event for the entire grid.
37268          * @param {Roo.EventObject} e
37269          */
37270         "mousedown" : true,
37271         /**
37272          * @event mouseup
37273          * The raw mouseup event for the entire grid.
37274          * @param {Roo.EventObject} e
37275          */
37276         "mouseup" : true,
37277         /**
37278          * @event mouseover
37279          * The raw mouseover event for the entire grid.
37280          * @param {Roo.EventObject} e
37281          */
37282         "mouseover" : true,
37283         /**
37284          * @event mouseout
37285          * The raw mouseout event for the entire grid.
37286          * @param {Roo.EventObject} e
37287          */
37288         "mouseout" : true,
37289         /**
37290          * @event keypress
37291          * The raw keypress event for the entire grid.
37292          * @param {Roo.EventObject} e
37293          */
37294         "keypress" : true,
37295         /**
37296          * @event keydown
37297          * The raw keydown event for the entire grid.
37298          * @param {Roo.EventObject} e
37299          */
37300         "keydown" : true,
37301
37302         // custom events
37303
37304         /**
37305          * @event cellclick
37306          * Fires when a cell is clicked
37307          * @param {Grid} this
37308          * @param {Number} rowIndex
37309          * @param {Number} columnIndex
37310          * @param {Roo.EventObject} e
37311          */
37312         "cellclick" : true,
37313         /**
37314          * @event celldblclick
37315          * Fires when a cell is double clicked
37316          * @param {Grid} this
37317          * @param {Number} rowIndex
37318          * @param {Number} columnIndex
37319          * @param {Roo.EventObject} e
37320          */
37321         "celldblclick" : true,
37322         /**
37323          * @event rowclick
37324          * Fires when a row is clicked
37325          * @param {Grid} this
37326          * @param {Number} rowIndex
37327          * @param {Roo.EventObject} e
37328          */
37329         "rowclick" : true,
37330         /**
37331          * @event rowdblclick
37332          * Fires when a row is double clicked
37333          * @param {Grid} this
37334          * @param {Number} rowIndex
37335          * @param {Roo.EventObject} e
37336          */
37337         "rowdblclick" : true,
37338         /**
37339          * @event headerclick
37340          * Fires when a header is clicked
37341          * @param {Grid} this
37342          * @param {Number} columnIndex
37343          * @param {Roo.EventObject} e
37344          */
37345         "headerclick" : true,
37346         /**
37347          * @event headerdblclick
37348          * Fires when a header cell is double clicked
37349          * @param {Grid} this
37350          * @param {Number} columnIndex
37351          * @param {Roo.EventObject} e
37352          */
37353         "headerdblclick" : true,
37354         /**
37355          * @event rowcontextmenu
37356          * Fires when a row is right clicked
37357          * @param {Grid} this
37358          * @param {Number} rowIndex
37359          * @param {Roo.EventObject} e
37360          */
37361         "rowcontextmenu" : true,
37362         /**
37363          * @event cellcontextmenu
37364          * Fires when a cell is right clicked
37365          * @param {Grid} this
37366          * @param {Number} rowIndex
37367          * @param {Number} cellIndex
37368          * @param {Roo.EventObject} e
37369          */
37370          "cellcontextmenu" : true,
37371         /**
37372          * @event headercontextmenu
37373          * Fires when a header is right clicked
37374          * @param {Grid} this
37375          * @param {Number} columnIndex
37376          * @param {Roo.EventObject} e
37377          */
37378         "headercontextmenu" : true,
37379         /**
37380          * @event bodyscroll
37381          * Fires when the body element is scrolled
37382          * @param {Number} scrollLeft
37383          * @param {Number} scrollTop
37384          */
37385         "bodyscroll" : true,
37386         /**
37387          * @event columnresize
37388          * Fires when the user resizes a column
37389          * @param {Number} columnIndex
37390          * @param {Number} newSize
37391          */
37392         "columnresize" : true,
37393         /**
37394          * @event columnmove
37395          * Fires when the user moves a column
37396          * @param {Number} oldIndex
37397          * @param {Number} newIndex
37398          */
37399         "columnmove" : true,
37400         /**
37401          * @event startdrag
37402          * Fires when row(s) start being dragged
37403          * @param {Grid} this
37404          * @param {Roo.GridDD} dd The drag drop object
37405          * @param {event} e The raw browser event
37406          */
37407         "startdrag" : true,
37408         /**
37409          * @event enddrag
37410          * Fires when a drag operation is complete
37411          * @param {Grid} this
37412          * @param {Roo.GridDD} dd The drag drop object
37413          * @param {event} e The raw browser event
37414          */
37415         "enddrag" : true,
37416         /**
37417          * @event dragdrop
37418          * Fires when dragged row(s) are dropped on a valid DD target
37419          * @param {Grid} this
37420          * @param {Roo.GridDD} dd The drag drop object
37421          * @param {String} targetId The target drag drop object
37422          * @param {event} e The raw browser event
37423          */
37424         "dragdrop" : true,
37425         /**
37426          * @event dragover
37427          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37428          * @param {Grid} this
37429          * @param {Roo.GridDD} dd The drag drop object
37430          * @param {String} targetId The target drag drop object
37431          * @param {event} e The raw browser event
37432          */
37433         "dragover" : true,
37434         /**
37435          * @event dragenter
37436          *  Fires when the dragged row(s) first cross another DD target while being dragged
37437          * @param {Grid} this
37438          * @param {Roo.GridDD} dd The drag drop object
37439          * @param {String} targetId The target drag drop object
37440          * @param {event} e The raw browser event
37441          */
37442         "dragenter" : true,
37443         /**
37444          * @event dragout
37445          * Fires when the dragged row(s) leave another DD target while being dragged
37446          * @param {Grid} this
37447          * @param {Roo.GridDD} dd The drag drop object
37448          * @param {String} targetId The target drag drop object
37449          * @param {event} e The raw browser event
37450          */
37451         "dragout" : true,
37452         /**
37453          * @event rowclass
37454          * Fires when a row is rendered, so you can change add a style to it.
37455          * @param {GridView} gridview   The grid view
37456          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37457          */
37458         'rowclass' : true,
37459
37460         /**
37461          * @event render
37462          * Fires when the grid is rendered
37463          * @param {Grid} grid
37464          */
37465         'render' : true,
37466             /**
37467              * @event select
37468              * Fires when a date is selected
37469              * @param {DatePicker} this
37470              * @param {Date} date The selected date
37471              */
37472         'select': true,
37473         /**
37474              * @event monthchange
37475              * Fires when the displayed month changes 
37476              * @param {DatePicker} this
37477              * @param {Date} date The selected month
37478              */
37479         'monthchange': true,
37480         /**
37481              * @event evententer
37482              * Fires when mouse over an event
37483              * @param {Calendar} this
37484              * @param {event} Event
37485              */
37486         'evententer': true,
37487         /**
37488              * @event eventleave
37489              * Fires when the mouse leaves an
37490              * @param {Calendar} this
37491              * @param {event}
37492              */
37493         'eventleave': true,
37494         /**
37495              * @event eventclick
37496              * Fires when the mouse click an
37497              * @param {Calendar} this
37498              * @param {event}
37499              */
37500         'eventclick': true,
37501         /**
37502              * @event eventrender
37503              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37504              * @param {Calendar} this
37505              * @param {data} data to be modified
37506              */
37507         'eventrender': true
37508         
37509     });
37510
37511     Roo.grid.Grid.superclass.constructor.call(this);
37512     this.on('render', function() {
37513         this.view.el.addClass('x-grid-cal'); 
37514         
37515         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37516
37517     },this);
37518     
37519     if (!Roo.grid.Calendar.style) {
37520         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37521             
37522             
37523             '.x-grid-cal .x-grid-col' :  {
37524                 height: 'auto !important',
37525                 'vertical-align': 'top'
37526             },
37527             '.x-grid-cal  .fc-event-hori' : {
37528                 height: '14px'
37529             }
37530              
37531             
37532         }, Roo.id());
37533     }
37534
37535     
37536     
37537 };
37538 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37539     /**
37540      * @cfg {Store} eventStore The store that loads events.
37541      */
37542     eventStore : 25,
37543
37544      
37545     activeDate : false,
37546     startDay : 0,
37547     autoWidth : true,
37548     monitorWindowResize : false,
37549
37550     
37551     resizeColumns : function() {
37552         var col = (this.view.el.getWidth() / 7) - 3;
37553         // loop through cols, and setWidth
37554         for(var i =0 ; i < 7 ; i++){
37555             this.cm.setColumnWidth(i, col);
37556         }
37557     },
37558      setDate :function(date) {
37559         
37560         Roo.log('setDate?');
37561         
37562         this.resizeColumns();
37563         var vd = this.activeDate;
37564         this.activeDate = date;
37565 //        if(vd && this.el){
37566 //            var t = date.getTime();
37567 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37568 //                Roo.log('using add remove');
37569 //                
37570 //                this.fireEvent('monthchange', this, date);
37571 //                
37572 //                this.cells.removeClass("fc-state-highlight");
37573 //                this.cells.each(function(c){
37574 //                   if(c.dateValue == t){
37575 //                       c.addClass("fc-state-highlight");
37576 //                       setTimeout(function(){
37577 //                            try{c.dom.firstChild.focus();}catch(e){}
37578 //                       }, 50);
37579 //                       return false;
37580 //                   }
37581 //                   return true;
37582 //                });
37583 //                return;
37584 //            }
37585 //        }
37586         
37587         var days = date.getDaysInMonth();
37588         
37589         var firstOfMonth = date.getFirstDateOfMonth();
37590         var startingPos = firstOfMonth.getDay()-this.startDay;
37591         
37592         if(startingPos < this.startDay){
37593             startingPos += 7;
37594         }
37595         
37596         var pm = date.add(Date.MONTH, -1);
37597         var prevStart = pm.getDaysInMonth()-startingPos;
37598 //        
37599         
37600         
37601         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37602         
37603         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37604         //this.cells.addClassOnOver('fc-state-hover');
37605         
37606         var cells = this.cells.elements;
37607         var textEls = this.textNodes;
37608         
37609         //Roo.each(cells, function(cell){
37610         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37611         //});
37612         
37613         days += startingPos;
37614
37615         // convert everything to numbers so it's fast
37616         var day = 86400000;
37617         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37618         //Roo.log(d);
37619         //Roo.log(pm);
37620         //Roo.log(prevStart);
37621         
37622         var today = new Date().clearTime().getTime();
37623         var sel = date.clearTime().getTime();
37624         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37625         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37626         var ddMatch = this.disabledDatesRE;
37627         var ddText = this.disabledDatesText;
37628         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37629         var ddaysText = this.disabledDaysText;
37630         var format = this.format;
37631         
37632         var setCellClass = function(cal, cell){
37633             
37634             //Roo.log('set Cell Class');
37635             cell.title = "";
37636             var t = d.getTime();
37637             
37638             //Roo.log(d);
37639             
37640             
37641             cell.dateValue = t;
37642             if(t == today){
37643                 cell.className += " fc-today";
37644                 cell.className += " fc-state-highlight";
37645                 cell.title = cal.todayText;
37646             }
37647             if(t == sel){
37648                 // disable highlight in other month..
37649                 cell.className += " fc-state-highlight";
37650                 
37651             }
37652             // disabling
37653             if(t < min) {
37654                 //cell.className = " fc-state-disabled";
37655                 cell.title = cal.minText;
37656                 return;
37657             }
37658             if(t > max) {
37659                 //cell.className = " fc-state-disabled";
37660                 cell.title = cal.maxText;
37661                 return;
37662             }
37663             if(ddays){
37664                 if(ddays.indexOf(d.getDay()) != -1){
37665                     // cell.title = ddaysText;
37666                    // cell.className = " fc-state-disabled";
37667                 }
37668             }
37669             if(ddMatch && format){
37670                 var fvalue = d.dateFormat(format);
37671                 if(ddMatch.test(fvalue)){
37672                     cell.title = ddText.replace("%0", fvalue);
37673                    cell.className = " fc-state-disabled";
37674                 }
37675             }
37676             
37677             if (!cell.initialClassName) {
37678                 cell.initialClassName = cell.dom.className;
37679             }
37680             
37681             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37682         };
37683
37684         var i = 0;
37685         
37686         for(; i < startingPos; i++) {
37687             cells[i].dayName =  (++prevStart);
37688             Roo.log(textEls[i]);
37689             d.setDate(d.getDate()+1);
37690             
37691             //cells[i].className = "fc-past fc-other-month";
37692             setCellClass(this, cells[i]);
37693         }
37694         
37695         var intDay = 0;
37696         
37697         for(; i < days; i++){
37698             intDay = i - startingPos + 1;
37699             cells[i].dayName =  (intDay);
37700             d.setDate(d.getDate()+1);
37701             
37702             cells[i].className = ''; // "x-date-active";
37703             setCellClass(this, cells[i]);
37704         }
37705         var extraDays = 0;
37706         
37707         for(; i < 42; i++) {
37708             //textEls[i].innerHTML = (++extraDays);
37709             
37710             d.setDate(d.getDate()+1);
37711             cells[i].dayName = (++extraDays);
37712             cells[i].className = "fc-future fc-other-month";
37713             setCellClass(this, cells[i]);
37714         }
37715         
37716         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37717         
37718         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37719         
37720         // this will cause all the cells to mis
37721         var rows= [];
37722         var i =0;
37723         for (var r = 0;r < 6;r++) {
37724             for (var c =0;c < 7;c++) {
37725                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37726             }    
37727         }
37728         
37729         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37730         for(i=0;i<cells.length;i++) {
37731             
37732             this.cells.elements[i].dayName = cells[i].dayName ;
37733             this.cells.elements[i].className = cells[i].className;
37734             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37735             this.cells.elements[i].title = cells[i].title ;
37736             this.cells.elements[i].dateValue = cells[i].dateValue ;
37737         }
37738         
37739         
37740         
37741         
37742         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37743         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37744         
37745         ////if(totalRows != 6){
37746             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37747            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37748        // }
37749         
37750         this.fireEvent('monthchange', this, date);
37751         
37752         
37753     },
37754  /**
37755      * Returns the grid's SelectionModel.
37756      * @return {SelectionModel}
37757      */
37758     getSelectionModel : function(){
37759         if(!this.selModel){
37760             this.selModel = new Roo.grid.CellSelectionModel();
37761         }
37762         return this.selModel;
37763     },
37764
37765     load: function() {
37766         this.eventStore.load()
37767         
37768         
37769         
37770     },
37771     
37772     findCell : function(dt) {
37773         dt = dt.clearTime().getTime();
37774         var ret = false;
37775         this.cells.each(function(c){
37776             //Roo.log("check " +c.dateValue + '?=' + dt);
37777             if(c.dateValue == dt){
37778                 ret = c;
37779                 return false;
37780             }
37781             return true;
37782         });
37783         
37784         return ret;
37785     },
37786     
37787     findCells : function(rec) {
37788         var s = rec.data.start_dt.clone().clearTime().getTime();
37789        // Roo.log(s);
37790         var e= rec.data.end_dt.clone().clearTime().getTime();
37791        // Roo.log(e);
37792         var ret = [];
37793         this.cells.each(function(c){
37794              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37795             
37796             if(c.dateValue > e){
37797                 return ;
37798             }
37799             if(c.dateValue < s){
37800                 return ;
37801             }
37802             ret.push(c);
37803         });
37804         
37805         return ret;    
37806     },
37807     
37808     findBestRow: function(cells)
37809     {
37810         var ret = 0;
37811         
37812         for (var i =0 ; i < cells.length;i++) {
37813             ret  = Math.max(cells[i].rows || 0,ret);
37814         }
37815         return ret;
37816         
37817     },
37818     
37819     
37820     addItem : function(rec)
37821     {
37822         // look for vertical location slot in
37823         var cells = this.findCells(rec);
37824         
37825         rec.row = this.findBestRow(cells);
37826         
37827         // work out the location.
37828         
37829         var crow = false;
37830         var rows = [];
37831         for(var i =0; i < cells.length; i++) {
37832             if (!crow) {
37833                 crow = {
37834                     start : cells[i],
37835                     end :  cells[i]
37836                 };
37837                 continue;
37838             }
37839             if (crow.start.getY() == cells[i].getY()) {
37840                 // on same row.
37841                 crow.end = cells[i];
37842                 continue;
37843             }
37844             // different row.
37845             rows.push(crow);
37846             crow = {
37847                 start: cells[i],
37848                 end : cells[i]
37849             };
37850             
37851         }
37852         
37853         rows.push(crow);
37854         rec.els = [];
37855         rec.rows = rows;
37856         rec.cells = cells;
37857         for (var i = 0; i < cells.length;i++) {
37858             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37859             
37860         }
37861         
37862         
37863     },
37864     
37865     clearEvents: function() {
37866         
37867         if (!this.eventStore.getCount()) {
37868             return;
37869         }
37870         // reset number of rows in cells.
37871         Roo.each(this.cells.elements, function(c){
37872             c.rows = 0;
37873         });
37874         
37875         this.eventStore.each(function(e) {
37876             this.clearEvent(e);
37877         },this);
37878         
37879     },
37880     
37881     clearEvent : function(ev)
37882     {
37883         if (ev.els) {
37884             Roo.each(ev.els, function(el) {
37885                 el.un('mouseenter' ,this.onEventEnter, this);
37886                 el.un('mouseleave' ,this.onEventLeave, this);
37887                 el.remove();
37888             },this);
37889             ev.els = [];
37890         }
37891     },
37892     
37893     
37894     renderEvent : function(ev,ctr) {
37895         if (!ctr) {
37896              ctr = this.view.el.select('.fc-event-container',true).first();
37897         }
37898         
37899          
37900         this.clearEvent(ev);
37901             //code
37902        
37903         
37904         
37905         ev.els = [];
37906         var cells = ev.cells;
37907         var rows = ev.rows;
37908         this.fireEvent('eventrender', this, ev);
37909         
37910         for(var i =0; i < rows.length; i++) {
37911             
37912             cls = '';
37913             if (i == 0) {
37914                 cls += ' fc-event-start';
37915             }
37916             if ((i+1) == rows.length) {
37917                 cls += ' fc-event-end';
37918             }
37919             
37920             //Roo.log(ev.data);
37921             // how many rows should it span..
37922             var cg = this.eventTmpl.append(ctr,Roo.apply({
37923                 fccls : cls
37924                 
37925             }, ev.data) , true);
37926             
37927             
37928             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37929             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37930             cg.on('click', this.onEventClick, this, ev);
37931             
37932             ev.els.push(cg);
37933             
37934             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37935             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37936             //Roo.log(cg);
37937              
37938             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
37939             cg.setWidth(ebox.right - sbox.x -2);
37940         }
37941     },
37942     
37943     renderEvents: function()
37944     {   
37945         // first make sure there is enough space..
37946         
37947         if (!this.eventTmpl) {
37948             this.eventTmpl = new Roo.Template(
37949                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
37950                     '<div class="fc-event-inner">' +
37951                         '<span class="fc-event-time">{time}</span>' +
37952                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37953                     '</div>' +
37954                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
37955                 '</div>'
37956             );
37957                 
37958         }
37959                
37960         
37961         
37962         this.cells.each(function(c) {
37963             //Roo.log(c.select('.fc-day-content div',true).first());
37964             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37965         });
37966         
37967         var ctr = this.view.el.select('.fc-event-container',true).first();
37968         
37969         var cls;
37970         this.eventStore.each(function(ev){
37971             
37972             this.renderEvent(ev);
37973              
37974              
37975         }, this);
37976         this.view.layout();
37977         
37978     },
37979     
37980     onEventEnter: function (e, el,event,d) {
37981         this.fireEvent('evententer', this, el, event);
37982     },
37983     
37984     onEventLeave: function (e, el,event,d) {
37985         this.fireEvent('eventleave', this, el, event);
37986     },
37987     
37988     onEventClick: function (e, el,event,d) {
37989         this.fireEvent('eventclick', this, el, event);
37990     },
37991     
37992     onMonthChange: function () {
37993         this.store.load();
37994     },
37995     
37996     onLoad: function () {
37997         
37998         //Roo.log('calendar onload');
37999 //         
38000         if(this.eventStore.getCount() > 0){
38001             
38002            
38003             
38004             this.eventStore.each(function(d){
38005                 
38006                 
38007                 // FIXME..
38008                 var add =   d.data;
38009                 if (typeof(add.end_dt) == 'undefined')  {
38010                     Roo.log("Missing End time in calendar data: ");
38011                     Roo.log(d);
38012                     return;
38013                 }
38014                 if (typeof(add.start_dt) == 'undefined')  {
38015                     Roo.log("Missing Start time in calendar data: ");
38016                     Roo.log(d);
38017                     return;
38018                 }
38019                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38020                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38021                 add.id = add.id || d.id;
38022                 add.title = add.title || '??';
38023                 
38024                 this.addItem(d);
38025                 
38026              
38027             },this);
38028         }
38029         
38030         this.renderEvents();
38031     }
38032     
38033
38034 });
38035 /*
38036  grid : {
38037                 xtype: 'Grid',
38038                 xns: Roo.grid,
38039                 listeners : {
38040                     render : function ()
38041                     {
38042                         _this.grid = this;
38043                         
38044                         if (!this.view.el.hasClass('course-timesheet')) {
38045                             this.view.el.addClass('course-timesheet');
38046                         }
38047                         if (this.tsStyle) {
38048                             this.ds.load({});
38049                             return; 
38050                         }
38051                         Roo.log('width');
38052                         Roo.log(_this.grid.view.el.getWidth());
38053                         
38054                         
38055                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
38056                             '.course-timesheet .x-grid-row' : {
38057                                 height: '80px'
38058                             },
38059                             '.x-grid-row td' : {
38060                                 'vertical-align' : 0
38061                             },
38062                             '.course-edit-link' : {
38063                                 'color' : 'blue',
38064                                 'text-overflow' : 'ellipsis',
38065                                 'overflow' : 'hidden',
38066                                 'white-space' : 'nowrap',
38067                                 'cursor' : 'pointer'
38068                             },
38069                             '.sub-link' : {
38070                                 'color' : 'green'
38071                             },
38072                             '.de-act-sup-link' : {
38073                                 'color' : 'purple',
38074                                 'text-decoration' : 'line-through'
38075                             },
38076                             '.de-act-link' : {
38077                                 'color' : 'red',
38078                                 'text-decoration' : 'line-through'
38079                             },
38080                             '.course-timesheet .course-highlight' : {
38081                                 'border-top-style': 'dashed !important',
38082                                 'border-bottom-bottom': 'dashed !important'
38083                             },
38084                             '.course-timesheet .course-item' : {
38085                                 'font-family'   : 'tahoma, arial, helvetica',
38086                                 'font-size'     : '11px',
38087                                 'overflow'      : 'hidden',
38088                                 'padding-left'  : '10px',
38089                                 'padding-right' : '10px',
38090                                 'padding-top' : '10px' 
38091                             }
38092                             
38093                         }, Roo.id());
38094                                 this.ds.load({});
38095                     }
38096                 },
38097                 autoWidth : true,
38098                 monitorWindowResize : false,
38099                 cellrenderer : function(v,x,r)
38100                 {
38101                     return v;
38102                 },
38103                 sm : {
38104                     xtype: 'CellSelectionModel',
38105                     xns: Roo.grid
38106                 },
38107                 dataSource : {
38108                     xtype: 'Store',
38109                     xns: Roo.data,
38110                     listeners : {
38111                         beforeload : function (_self, options)
38112                         {
38113                             options.params = options.params || {};
38114                             options.params._month = _this.monthField.getValue();
38115                             options.params.limit = 9999;
38116                             options.params['sort'] = 'when_dt';    
38117                             options.params['dir'] = 'ASC';    
38118                             this.proxy.loadResponse = this.loadResponse;
38119                             Roo.log("load?");
38120                             //this.addColumns();
38121                         },
38122                         load : function (_self, records, options)
38123                         {
38124                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38125                                 // if you click on the translation.. you can edit it...
38126                                 var el = Roo.get(this);
38127                                 var id = el.dom.getAttribute('data-id');
38128                                 var d = el.dom.getAttribute('data-date');
38129                                 var t = el.dom.getAttribute('data-time');
38130                                 //var id = this.child('span').dom.textContent;
38131                                 
38132                                 //Roo.log(this);
38133                                 Pman.Dialog.CourseCalendar.show({
38134                                     id : id,
38135                                     when_d : d,
38136                                     when_t : t,
38137                                     productitem_active : id ? 1 : 0
38138                                 }, function() {
38139                                     _this.grid.ds.load({});
38140                                 });
38141                            
38142                            });
38143                            
38144                            _this.panel.fireEvent('resize', [ '', '' ]);
38145                         }
38146                     },
38147                     loadResponse : function(o, success, response){
38148                             // this is overridden on before load..
38149                             
38150                             Roo.log("our code?");       
38151                             //Roo.log(success);
38152                             //Roo.log(response)
38153                             delete this.activeRequest;
38154                             if(!success){
38155                                 this.fireEvent("loadexception", this, o, response);
38156                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38157                                 return;
38158                             }
38159                             var result;
38160                             try {
38161                                 result = o.reader.read(response);
38162                             }catch(e){
38163                                 Roo.log("load exception?");
38164                                 this.fireEvent("loadexception", this, o, response, e);
38165                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38166                                 return;
38167                             }
38168                             Roo.log("ready...");        
38169                             // loop through result.records;
38170                             // and set this.tdate[date] = [] << array of records..
38171                             _this.tdata  = {};
38172                             Roo.each(result.records, function(r){
38173                                 //Roo.log(r.data);
38174                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38175                                     _this.tdata[r.data.when_dt.format('j')] = [];
38176                                 }
38177                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38178                             });
38179                             
38180                             //Roo.log(_this.tdata);
38181                             
38182                             result.records = [];
38183                             result.totalRecords = 6;
38184                     
38185                             // let's generate some duumy records for the rows.
38186                             //var st = _this.dateField.getValue();
38187                             
38188                             // work out monday..
38189                             //st = st.add(Date.DAY, -1 * st.format('w'));
38190                             
38191                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38192                             
38193                             var firstOfMonth = date.getFirstDayOfMonth();
38194                             var days = date.getDaysInMonth();
38195                             var d = 1;
38196                             var firstAdded = false;
38197                             for (var i = 0; i < result.totalRecords ; i++) {
38198                                 //var d= st.add(Date.DAY, i);
38199                                 var row = {};
38200                                 var added = 0;
38201                                 for(var w = 0 ; w < 7 ; w++){
38202                                     if(!firstAdded && firstOfMonth != w){
38203                                         continue;
38204                                     }
38205                                     if(d > days){
38206                                         continue;
38207                                     }
38208                                     firstAdded = true;
38209                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
38210                                     row['weekday'+w] = String.format(
38211                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
38212                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38213                                                     d,
38214                                                     date.format('Y-m-')+dd
38215                                                 );
38216                                     added++;
38217                                     if(typeof(_this.tdata[d]) != 'undefined'){
38218                                         Roo.each(_this.tdata[d], function(r){
38219                                             var is_sub = '';
38220                                             var deactive = '';
38221                                             var id = r.id;
38222                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38223                                             if(r.parent_id*1>0){
38224                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38225                                                 id = r.parent_id;
38226                                             }
38227                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38228                                                 deactive = 'de-act-link';
38229                                             }
38230                                             
38231                                             row['weekday'+w] += String.format(
38232                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38233                                                     id, //0
38234                                                     r.product_id_name, //1
38235                                                     r.when_dt.format('h:ia'), //2
38236                                                     is_sub, //3
38237                                                     deactive, //4
38238                                                     desc // 5
38239                                             );
38240                                         });
38241                                     }
38242                                     d++;
38243                                 }
38244                                 
38245                                 // only do this if something added..
38246                                 if(added > 0){ 
38247                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38248                                 }
38249                                 
38250                                 
38251                                 // push it twice. (second one with an hour..
38252                                 
38253                             }
38254                             //Roo.log(result);
38255                             this.fireEvent("load", this, o, o.request.arg);
38256                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38257                         },
38258                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38259                     proxy : {
38260                         xtype: 'HttpProxy',
38261                         xns: Roo.data,
38262                         method : 'GET',
38263                         url : baseURL + '/Roo/Shop_course.php'
38264                     },
38265                     reader : {
38266                         xtype: 'JsonReader',
38267                         xns: Roo.data,
38268                         id : 'id',
38269                         fields : [
38270                             {
38271                                 'name': 'id',
38272                                 'type': 'int'
38273                             },
38274                             {
38275                                 'name': 'when_dt',
38276                                 'type': 'string'
38277                             },
38278                             {
38279                                 'name': 'end_dt',
38280                                 'type': 'string'
38281                             },
38282                             {
38283                                 'name': 'parent_id',
38284                                 'type': 'int'
38285                             },
38286                             {
38287                                 'name': 'product_id',
38288                                 'type': 'int'
38289                             },
38290                             {
38291                                 'name': 'productitem_id',
38292                                 'type': 'int'
38293                             },
38294                             {
38295                                 'name': 'guid',
38296                                 'type': 'int'
38297                             }
38298                         ]
38299                     }
38300                 },
38301                 toolbar : {
38302                     xtype: 'Toolbar',
38303                     xns: Roo,
38304                     items : [
38305                         {
38306                             xtype: 'Button',
38307                             xns: Roo.Toolbar,
38308                             listeners : {
38309                                 click : function (_self, e)
38310                                 {
38311                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38312                                     sd.setMonth(sd.getMonth()-1);
38313                                     _this.monthField.setValue(sd.format('Y-m-d'));
38314                                     _this.grid.ds.load({});
38315                                 }
38316                             },
38317                             text : "Back"
38318                         },
38319                         {
38320                             xtype: 'Separator',
38321                             xns: Roo.Toolbar
38322                         },
38323                         {
38324                             xtype: 'MonthField',
38325                             xns: Roo.form,
38326                             listeners : {
38327                                 render : function (_self)
38328                                 {
38329                                     _this.monthField = _self;
38330                                    // _this.monthField.set  today
38331                                 },
38332                                 select : function (combo, date)
38333                                 {
38334                                     _this.grid.ds.load({});
38335                                 }
38336                             },
38337                             value : (function() { return new Date(); })()
38338                         },
38339                         {
38340                             xtype: 'Separator',
38341                             xns: Roo.Toolbar
38342                         },
38343                         {
38344                             xtype: 'TextItem',
38345                             xns: Roo.Toolbar,
38346                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38347                         },
38348                         {
38349                             xtype: 'Fill',
38350                             xns: Roo.Toolbar
38351                         },
38352                         {
38353                             xtype: 'Button',
38354                             xns: Roo.Toolbar,
38355                             listeners : {
38356                                 click : function (_self, e)
38357                                 {
38358                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38359                                     sd.setMonth(sd.getMonth()+1);
38360                                     _this.monthField.setValue(sd.format('Y-m-d'));
38361                                     _this.grid.ds.load({});
38362                                 }
38363                             },
38364                             text : "Next"
38365                         }
38366                     ]
38367                 },
38368                  
38369             }
38370         };
38371         
38372         *//*
38373  * Based on:
38374  * Ext JS Library 1.1.1
38375  * Copyright(c) 2006-2007, Ext JS, LLC.
38376  *
38377  * Originally Released Under LGPL - original licence link has changed is not relivant.
38378  *
38379  * Fork - LGPL
38380  * <script type="text/javascript">
38381  */
38382  
38383 /**
38384  * @class Roo.LoadMask
38385  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38386  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38387  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38388  * element's UpdateManager load indicator and will be destroyed after the initial load.
38389  * @constructor
38390  * Create a new LoadMask
38391  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38392  * @param {Object} config The config object
38393  */
38394 Roo.LoadMask = function(el, config){
38395     this.el = Roo.get(el);
38396     Roo.apply(this, config);
38397     if(this.store){
38398         this.store.on('beforeload', this.onBeforeLoad, this);
38399         this.store.on('load', this.onLoad, this);
38400         this.store.on('loadexception', this.onLoadException, this);
38401         this.removeMask = false;
38402     }else{
38403         var um = this.el.getUpdateManager();
38404         um.showLoadIndicator = false; // disable the default indicator
38405         um.on('beforeupdate', this.onBeforeLoad, this);
38406         um.on('update', this.onLoad, this);
38407         um.on('failure', this.onLoad, this);
38408         this.removeMask = true;
38409     }
38410 };
38411
38412 Roo.LoadMask.prototype = {
38413     /**
38414      * @cfg {Boolean} removeMask
38415      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38416      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38417      */
38418     removeMask : false,
38419     /**
38420      * @cfg {String} msg
38421      * The text to display in a centered loading message box (defaults to 'Loading...')
38422      */
38423     msg : 'Loading...',
38424     /**
38425      * @cfg {String} msgCls
38426      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38427      */
38428     msgCls : 'x-mask-loading',
38429
38430     /**
38431      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38432      * @type Boolean
38433      */
38434     disabled: false,
38435
38436     /**
38437      * Disables the mask to prevent it from being displayed
38438      */
38439     disable : function(){
38440        this.disabled = true;
38441     },
38442
38443     /**
38444      * Enables the mask so that it can be displayed
38445      */
38446     enable : function(){
38447         this.disabled = false;
38448     },
38449     
38450     onLoadException : function()
38451     {
38452         Roo.log(arguments);
38453         
38454         if (typeof(arguments[3]) != 'undefined') {
38455             Roo.MessageBox.alert("Error loading",arguments[3]);
38456         } 
38457         /*
38458         try {
38459             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38460                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38461             }   
38462         } catch(e) {
38463             
38464         }
38465         */
38466     
38467         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38468     },
38469     // private
38470     onLoad : function()
38471     {
38472         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38473     },
38474
38475     // private
38476     onBeforeLoad : function(){
38477         if(!this.disabled){
38478             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38479         }
38480     },
38481
38482     // private
38483     destroy : function(){
38484         if(this.store){
38485             this.store.un('beforeload', this.onBeforeLoad, this);
38486             this.store.un('load', this.onLoad, this);
38487             this.store.un('loadexception', this.onLoadException, this);
38488         }else{
38489             var um = this.el.getUpdateManager();
38490             um.un('beforeupdate', this.onBeforeLoad, this);
38491             um.un('update', this.onLoad, this);
38492             um.un('failure', this.onLoad, this);
38493         }
38494     }
38495 };/*
38496  * Based on:
38497  * Ext JS Library 1.1.1
38498  * Copyright(c) 2006-2007, Ext JS, LLC.
38499  *
38500  * Originally Released Under LGPL - original licence link has changed is not relivant.
38501  *
38502  * Fork - LGPL
38503  * <script type="text/javascript">
38504  */
38505
38506
38507 /**
38508  * @class Roo.XTemplate
38509  * @extends Roo.Template
38510  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38511 <pre><code>
38512 var t = new Roo.XTemplate(
38513         '&lt;select name="{name}"&gt;',
38514                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38515         '&lt;/select&gt;'
38516 );
38517  
38518 // then append, applying the master template values
38519  </code></pre>
38520  *
38521  * Supported features:
38522  *
38523  *  Tags:
38524
38525 <pre><code>
38526       {a_variable} - output encoded.
38527       {a_variable.format:("Y-m-d")} - call a method on the variable
38528       {a_variable:raw} - unencoded output
38529       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38530       {a_variable:this.method_on_template(...)} - call a method on the template object.
38531  
38532 </code></pre>
38533  *  The tpl tag:
38534 <pre><code>
38535         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38536         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38537         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38538         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38539   
38540         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38541         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38542 </code></pre>
38543  *      
38544  */
38545 Roo.XTemplate = function()
38546 {
38547     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38548     if (this.html) {
38549         this.compile();
38550     }
38551 };
38552
38553
38554 Roo.extend(Roo.XTemplate, Roo.Template, {
38555
38556     /**
38557      * The various sub templates
38558      */
38559     tpls : false,
38560     /**
38561      *
38562      * basic tag replacing syntax
38563      * WORD:WORD()
38564      *
38565      * // you can fake an object call by doing this
38566      *  x.t:(test,tesT) 
38567      * 
38568      */
38569     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38570
38571     /**
38572      * compile the template
38573      *
38574      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38575      *
38576      */
38577     compile: function()
38578     {
38579         var s = this.html;
38580      
38581         s = ['<tpl>', s, '</tpl>'].join('');
38582     
38583         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38584             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38585             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38586             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38587             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38588             m,
38589             id     = 0,
38590             tpls   = [];
38591     
38592         while(true == !!(m = s.match(re))){
38593             var forMatch   = m[0].match(nameRe),
38594                 ifMatch   = m[0].match(ifRe),
38595                 execMatch   = m[0].match(execRe),
38596                 namedMatch   = m[0].match(namedRe),
38597                 
38598                 exp  = null, 
38599                 fn   = null,
38600                 exec = null,
38601                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38602                 
38603             if (ifMatch) {
38604                 // if - puts fn into test..
38605                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38606                 if(exp){
38607                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38608                 }
38609             }
38610             
38611             if (execMatch) {
38612                 // exec - calls a function... returns empty if true is  returned.
38613                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38614                 if(exp){
38615                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38616                 }
38617             }
38618             
38619             
38620             if (name) {
38621                 // for = 
38622                 switch(name){
38623                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38624                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38625                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38626                 }
38627             }
38628             var uid = namedMatch ? namedMatch[1] : id;
38629             
38630             
38631             tpls.push({
38632                 id:     namedMatch ? namedMatch[1] : id,
38633                 target: name,
38634                 exec:   exec,
38635                 test:   fn,
38636                 body:   m[1] || ''
38637             });
38638             if (namedMatch) {
38639                 s = s.replace(m[0], '');
38640             } else { 
38641                 s = s.replace(m[0], '{xtpl'+ id + '}');
38642             }
38643             ++id;
38644         }
38645         this.tpls = [];
38646         for(var i = tpls.length-1; i >= 0; --i){
38647             this.compileTpl(tpls[i]);
38648             this.tpls[tpls[i].id] = tpls[i];
38649         }
38650         this.master = tpls[tpls.length-1];
38651         return this;
38652     },
38653     /**
38654      * same as applyTemplate, except it's done to one of the subTemplates
38655      * when using named templates, you can do:
38656      *
38657      * var str = pl.applySubTemplate('your-name', values);
38658      *
38659      * 
38660      * @param {Number} id of the template
38661      * @param {Object} values to apply to template
38662      * @param {Object} parent (normaly the instance of this object)
38663      */
38664     applySubTemplate : function(id, values, parent)
38665     {
38666         
38667         
38668         var t = this.tpls[id];
38669         
38670         
38671         try { 
38672             if(t.test && !t.test.call(this, values, parent)){
38673                 return '';
38674             }
38675         } catch(e) {
38676             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38677             Roo.log(e.toString());
38678             Roo.log(t.test);
38679             return ''
38680         }
38681         try { 
38682             
38683             if(t.exec && t.exec.call(this, values, parent)){
38684                 return '';
38685             }
38686         } catch(e) {
38687             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38688             Roo.log(e.toString());
38689             Roo.log(t.exec);
38690             return ''
38691         }
38692         try {
38693             var vs = t.target ? t.target.call(this, values, parent) : values;
38694             parent = t.target ? values : parent;
38695             if(t.target && vs instanceof Array){
38696                 var buf = [];
38697                 for(var i = 0, len = vs.length; i < len; i++){
38698                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38699                 }
38700                 return buf.join('');
38701             }
38702             return t.compiled.call(this, vs, parent);
38703         } catch (e) {
38704             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38705             Roo.log(e.toString());
38706             Roo.log(t.compiled);
38707             return '';
38708         }
38709     },
38710
38711     compileTpl : function(tpl)
38712     {
38713         var fm = Roo.util.Format;
38714         var useF = this.disableFormats !== true;
38715         var sep = Roo.isGecko ? "+" : ",";
38716         var undef = function(str) {
38717             Roo.log("Property not found :"  + str);
38718             return '';
38719         };
38720         
38721         var fn = function(m, name, format, args)
38722         {
38723             //Roo.log(arguments);
38724             args = args ? args.replace(/\\'/g,"'") : args;
38725             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38726             if (typeof(format) == 'undefined') {
38727                 format= 'htmlEncode';
38728             }
38729             if (format == 'raw' ) {
38730                 format = false;
38731             }
38732             
38733             if(name.substr(0, 4) == 'xtpl'){
38734                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38735             }
38736             
38737             // build an array of options to determine if value is undefined..
38738             
38739             // basically get 'xxxx.yyyy' then do
38740             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38741             //    (function () { Roo.log("Property not found"); return ''; })() :
38742             //    ......
38743             
38744             var udef_ar = [];
38745             var lookfor = '';
38746             Roo.each(name.split('.'), function(st) {
38747                 lookfor += (lookfor.length ? '.': '') + st;
38748                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38749             });
38750             
38751             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38752             
38753             
38754             if(format && useF){
38755                 
38756                 args = args ? ',' + args : "";
38757                  
38758                 if(format.substr(0, 5) != "this."){
38759                     format = "fm." + format + '(';
38760                 }else{
38761                     format = 'this.call("'+ format.substr(5) + '", ';
38762                     args = ", values";
38763                 }
38764                 
38765                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38766             }
38767              
38768             if (args.length) {
38769                 // called with xxyx.yuu:(test,test)
38770                 // change to ()
38771                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38772             }
38773             // raw.. - :raw modifier..
38774             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38775             
38776         };
38777         var body;
38778         // branched to use + in gecko and [].join() in others
38779         if(Roo.isGecko){
38780             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38781                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38782                     "';};};";
38783         }else{
38784             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38785             body.push(tpl.body.replace(/(\r\n|\n)/g,
38786                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38787             body.push("'].join('');};};");
38788             body = body.join('');
38789         }
38790         
38791         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38792        
38793         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38794         eval(body);
38795         
38796         return this;
38797     },
38798
38799     applyTemplate : function(values){
38800         return this.master.compiled.call(this, values, {});
38801         //var s = this.subs;
38802     },
38803
38804     apply : function(){
38805         return this.applyTemplate.apply(this, arguments);
38806     }
38807
38808  });
38809
38810 Roo.XTemplate.from = function(el){
38811     el = Roo.getDom(el);
38812     return new Roo.XTemplate(el.value || el.innerHTML);
38813 };