roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @static
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 [required] 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.DataReader} reader [required]  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          
716         if(!o){
717             if(success !== false){
718                 this.fireEvent("load", this, [], options, o);
719             }
720             if(options.callback){
721                 options.callback.call(options.scope || this, [], options, false);
722             }
723             return;
724         }
725         // if data returned failure - throw an exception.
726         if (o.success === false) {
727             // show a message if no listener is registered.
728             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
729                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
730             }
731             // loadmask wil be hooked into this..
732             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
733             return;
734         }
735         var r = o.records, t = o.totalRecords || r.length;
736         
737         this.fireEvent("beforeloadadd", this, r, options, o);
738         
739         if(!options || options.add !== true){
740             if(this.pruneModifiedRecords){
741                 this.modified = [];
742             }
743             for(var i = 0, len = r.length; i < len; i++){
744                 r[i].join(this);
745             }
746             if(this.snapshot){
747                 this.data = this.snapshot;
748                 delete this.snapshot;
749             }
750             this.data.clear();
751             this.data.addAll(r);
752             this.totalLength = t;
753             this.applySort();
754             this.fireEvent("datachanged", this);
755         }else{
756             this.totalLength = Math.max(t, this.data.length+r.length);
757             this.add(r);
758         }
759         
760         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
761                 
762             var e = new Roo.data.Record({});
763
764             e.set(this.parent.displayField, this.parent.emptyTitle);
765             e.set(this.parent.valueField, '');
766
767             this.insert(0, e);
768         }
769             
770         this.fireEvent("load", this, r, options, o);
771         if(options.callback){
772             options.callback.call(options.scope || this, r, options, true);
773         }
774     },
775
776
777     /**
778      * Loads data from a passed data block. A Reader which understands the format of the data
779      * must have been configured in the constructor.
780      * @param {Object} data The data block from which to read the Records.  The format of the data expected
781      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
782      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
783      */
784     loadData : function(o, append){
785         var r = this.reader.readRecords(o);
786         this.loadRecords(r, {add: append}, true);
787     },
788     
789      /**
790      * using 'cn' the nested child reader read the child array into it's child stores.
791      * @param {Object} rec The record with a 'children array
792      */
793     loadDataFromChildren : function(rec)
794     {
795         this.loadData(this.reader.toLoadData(rec));
796     },
797     
798
799     /**
800      * Gets the number of cached records.
801      * <p>
802      * <em>If using paging, this may not be the total size of the dataset. If the data object
803      * used by the Reader contains the dataset size, then the getTotalCount() function returns
804      * the data set size</em>
805      */
806     getCount : function(){
807         return this.data.length || 0;
808     },
809
810     /**
811      * Gets the total number of records in the dataset as returned by the server.
812      * <p>
813      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
814      * the dataset size</em>
815      */
816     getTotalCount : function(){
817         return this.totalLength || 0;
818     },
819
820     /**
821      * Returns the sort state of the Store as an object with two properties:
822      * <pre><code>
823  field {String} The name of the field by which the Records are sorted
824  direction {String} The sort order, "ASC" or "DESC"
825      * </code></pre>
826      */
827     getSortState : function(){
828         return this.sortInfo;
829     },
830
831     // private
832     applySort : function(){
833         if(this.sortInfo && !this.remoteSort){
834             var s = this.sortInfo, f = s.field;
835             var st = this.fields.get(f).sortType;
836             var fn = function(r1, r2){
837                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
838                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
839             };
840             this.data.sort(s.direction, fn);
841             if(this.snapshot && this.snapshot != this.data){
842                 this.snapshot.sort(s.direction, fn);
843             }
844         }
845     },
846
847     /**
848      * Sets the default sort column and order to be used by the next load operation.
849      * @param {String} fieldName The name of the field to sort by.
850      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
851      */
852     setDefaultSort : function(field, dir){
853         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
854     },
855
856     /**
857      * Sort the Records.
858      * If remote sorting is used, the sort is performed on the server, and the cache is
859      * reloaded. If local sorting is used, the cache is sorted internally.
860      * @param {String} fieldName The name of the field to sort by.
861      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
862      */
863     sort : function(fieldName, dir){
864         var f = this.fields.get(fieldName);
865         if(!dir){
866             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
867             
868             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
869                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
870             }else{
871                 dir = f.sortDir;
872             }
873         }
874         this.sortToggle[f.name] = dir;
875         this.sortInfo = {field: f.name, direction: dir};
876         if(!this.remoteSort){
877             this.applySort();
878             this.fireEvent("datachanged", this);
879         }else{
880             this.load(this.lastOptions);
881         }
882     },
883
884     /**
885      * Calls the specified function for each of the Records in the cache.
886      * @param {Function} fn The function to call. The Record is passed as the first parameter.
887      * Returning <em>false</em> aborts and exits the iteration.
888      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
889      */
890     each : function(fn, scope){
891         this.data.each(fn, scope);
892     },
893
894     /**
895      * Gets all records modified since the last commit.  Modified records are persisted across load operations
896      * (e.g., during paging).
897      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
898      */
899     getModifiedRecords : function(){
900         return this.modified;
901     },
902
903     // private
904     createFilterFn : function(property, value, anyMatch){
905         if(!value.exec){ // not a regex
906             value = String(value);
907             if(value.length == 0){
908                 return false;
909             }
910             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
911         }
912         return function(r){
913             return value.test(r.data[property]);
914         };
915     },
916
917     /**
918      * Sums the value of <i>property</i> for each record between start and end and returns the result.
919      * @param {String} property A field on your records
920      * @param {Number} start The record index to start at (defaults to 0)
921      * @param {Number} end The last record index to include (defaults to length - 1)
922      * @return {Number} The sum
923      */
924     sum : function(property, start, end){
925         var rs = this.data.items, v = 0;
926         start = start || 0;
927         end = (end || end === 0) ? end : rs.length-1;
928
929         for(var i = start; i <= end; i++){
930             v += (rs[i].data[property] || 0);
931         }
932         return v;
933     },
934
935     /**
936      * Filter the records by a specified property.
937      * @param {String} field A field on your records
938      * @param {String/RegExp} value Either a string that the field
939      * should start with or a RegExp to test against the field
940      * @param {Boolean} anyMatch True to match any part not just the beginning
941      */
942     filter : function(property, value, anyMatch){
943         var fn = this.createFilterFn(property, value, anyMatch);
944         return fn ? this.filterBy(fn) : this.clearFilter();
945     },
946
947     /**
948      * Filter by a function. The specified function will be called with each
949      * record in this data source. If the function returns true the record is included,
950      * otherwise it is filtered.
951      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
952      * @param {Object} scope (optional) The scope of the function (defaults to this)
953      */
954     filterBy : function(fn, scope){
955         this.snapshot = this.snapshot || this.data;
956         this.data = this.queryBy(fn, scope||this);
957         this.fireEvent("datachanged", this);
958     },
959
960     /**
961      * Query the records by a specified property.
962      * @param {String} field A field on your records
963      * @param {String/RegExp} value Either a string that the field
964      * should start with or a RegExp to test against the field
965      * @param {Boolean} anyMatch True to match any part not just the beginning
966      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
967      */
968     query : function(property, value, anyMatch){
969         var fn = this.createFilterFn(property, value, anyMatch);
970         return fn ? this.queryBy(fn) : this.data.clone();
971     },
972
973     /**
974      * Query by a function. The specified function will be called with each
975      * record in this data source. If the function returns true the record is included
976      * in the results.
977      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
978      * @param {Object} scope (optional) The scope of the function (defaults to this)
979       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
980      **/
981     queryBy : function(fn, scope){
982         var data = this.snapshot || this.data;
983         return data.filterBy(fn, scope||this);
984     },
985
986     /**
987      * Collects unique values for a particular dataIndex from this store.
988      * @param {String} dataIndex The property to collect
989      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
990      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
991      * @return {Array} An array of the unique values
992      **/
993     collect : function(dataIndex, allowNull, bypassFilter){
994         var d = (bypassFilter === true && this.snapshot) ?
995                 this.snapshot.items : this.data.items;
996         var v, sv, r = [], l = {};
997         for(var i = 0, len = d.length; i < len; i++){
998             v = d[i].data[dataIndex];
999             sv = String(v);
1000             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1001                 l[sv] = true;
1002                 r[r.length] = v;
1003             }
1004         }
1005         return r;
1006     },
1007
1008     /**
1009      * Revert to a view of the Record cache with no filtering applied.
1010      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1011      */
1012     clearFilter : function(suppressEvent){
1013         if(this.snapshot && this.snapshot != this.data){
1014             this.data = this.snapshot;
1015             delete this.snapshot;
1016             if(suppressEvent !== true){
1017                 this.fireEvent("datachanged", this);
1018             }
1019         }
1020     },
1021
1022     // private
1023     afterEdit : function(record){
1024         if(this.modified.indexOf(record) == -1){
1025             this.modified.push(record);
1026         }
1027         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1028     },
1029     
1030     // private
1031     afterReject : function(record){
1032         this.modified.remove(record);
1033         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1034     },
1035
1036     // private
1037     afterCommit : function(record){
1038         this.modified.remove(record);
1039         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1040     },
1041
1042     /**
1043      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1044      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1045      */
1046     commitChanges : function(){
1047         var m = this.modified.slice(0);
1048         this.modified = [];
1049         for(var i = 0, len = m.length; i < len; i++){
1050             m[i].commit();
1051         }
1052     },
1053
1054     /**
1055      * Cancel outstanding changes on all changed records.
1056      */
1057     rejectChanges : function(){
1058         var m = this.modified.slice(0);
1059         this.modified = [];
1060         for(var i = 0, len = m.length; i < len; i++){
1061             m[i].reject();
1062         }
1063     },
1064
1065     onMetaChange : function(meta, rtype, o){
1066         this.recordType = rtype;
1067         this.fields = rtype.prototype.fields;
1068         delete this.snapshot;
1069         this.sortInfo = meta.sortInfo || this.sortInfo;
1070         this.modified = [];
1071         this.fireEvent('metachange', this, this.reader.meta);
1072     },
1073     
1074     moveIndex : function(data, type)
1075     {
1076         var index = this.indexOf(data);
1077         
1078         var newIndex = index + type;
1079         
1080         this.remove(data);
1081         
1082         this.insert(newIndex, data);
1083         
1084     }
1085 });/*
1086  * Based on:
1087  * Ext JS Library 1.1.1
1088  * Copyright(c) 2006-2007, Ext JS, LLC.
1089  *
1090  * Originally Released Under LGPL - original licence link has changed is not relivant.
1091  *
1092  * Fork - LGPL
1093  * <script type="text/javascript">
1094  */
1095
1096 /**
1097  * @class Roo.data.SimpleStore
1098  * @extends Roo.data.Store
1099  * Small helper class to make creating Stores from Array data easier.
1100  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1101  * @cfg {Array} fields An array of field definition objects, or field name strings.
1102  * @cfg {Object} an existing reader (eg. copied from another store)
1103  * @cfg {Array} data The multi-dimensional array of data
1104  * @cfg {Roo.data.DataProxy} proxy [not-required]  
1105  * @cfg {Roo.data.Reader} reader  [not-required] 
1106  * @constructor
1107  * @param {Object} config
1108  */
1109 Roo.data.SimpleStore = function(config)
1110 {
1111     Roo.data.SimpleStore.superclass.constructor.call(this, {
1112         isLocal : true,
1113         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1114                 id: config.id
1115             },
1116             Roo.data.Record.create(config.fields)
1117         ),
1118         proxy : new Roo.data.MemoryProxy(config.data)
1119     });
1120     this.load();
1121 };
1122 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1123  * Based on:
1124  * Ext JS Library 1.1.1
1125  * Copyright(c) 2006-2007, Ext JS, LLC.
1126  *
1127  * Originally Released Under LGPL - original licence link has changed is not relivant.
1128  *
1129  * Fork - LGPL
1130  * <script type="text/javascript">
1131  */
1132
1133 /**
1134 /**
1135  * @extends Roo.data.Store
1136  * @class Roo.data.JsonStore
1137  * Small helper class to make creating Stores for JSON data easier. <br/>
1138 <pre><code>
1139 var store = new Roo.data.JsonStore({
1140     url: 'get-images.php',
1141     root: 'images',
1142     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1143 });
1144 </code></pre>
1145  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1146  * JsonReader and HttpProxy (unless inline data is provided).</b>
1147  * @cfg {Array} fields An array of field definition objects, or field name strings.
1148  * @constructor
1149  * @param {Object} config
1150  */
1151 Roo.data.JsonStore = function(c){
1152     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1153         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1154         reader: new Roo.data.JsonReader(c, c.fields)
1155     }));
1156 };
1157 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1158  * Based on:
1159  * Ext JS Library 1.1.1
1160  * Copyright(c) 2006-2007, Ext JS, LLC.
1161  *
1162  * Originally Released Under LGPL - original licence link has changed is not relivant.
1163  *
1164  * Fork - LGPL
1165  * <script type="text/javascript">
1166  */
1167
1168  
1169 Roo.data.Field = function(config){
1170     if(typeof config == "string"){
1171         config = {name: config};
1172     }
1173     Roo.apply(this, config);
1174     
1175     if(!this.type){
1176         this.type = "auto";
1177     }
1178     
1179     var st = Roo.data.SortTypes;
1180     // named sortTypes are supported, here we look them up
1181     if(typeof this.sortType == "string"){
1182         this.sortType = st[this.sortType];
1183     }
1184     
1185     // set default sortType for strings and dates
1186     if(!this.sortType){
1187         switch(this.type){
1188             case "string":
1189                 this.sortType = st.asUCString;
1190                 break;
1191             case "date":
1192                 this.sortType = st.asDate;
1193                 break;
1194             default:
1195                 this.sortType = st.none;
1196         }
1197     }
1198
1199     // define once
1200     var stripRe = /[\$,%]/g;
1201
1202     // prebuilt conversion function for this field, instead of
1203     // switching every time we're reading a value
1204     if(!this.convert){
1205         var cv, dateFormat = this.dateFormat;
1206         switch(this.type){
1207             case "":
1208             case "auto":
1209             case undefined:
1210                 cv = function(v){ return v; };
1211                 break;
1212             case "string":
1213                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1214                 break;
1215             case "int":
1216                 cv = function(v){
1217                     return v !== undefined && v !== null && v !== '' ?
1218                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1219                     };
1220                 break;
1221             case "float":
1222                 cv = function(v){
1223                     return v !== undefined && v !== null && v !== '' ?
1224                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1225                     };
1226                 break;
1227             case "bool":
1228             case "boolean":
1229                 cv = function(v){ return v === true || v === "true" || v == 1; };
1230                 break;
1231             case "date":
1232                 cv = function(v){
1233                     if(!v){
1234                         return '';
1235                     }
1236                     if(v instanceof Date){
1237                         return v;
1238                     }
1239                     if(dateFormat){
1240                         if(dateFormat == "timestamp"){
1241                             return new Date(v*1000);
1242                         }
1243                         return Date.parseDate(v, dateFormat);
1244                     }
1245                     var parsed = Date.parse(v);
1246                     return parsed ? new Date(parsed) : null;
1247                 };
1248              break;
1249             
1250         }
1251         this.convert = cv;
1252     }
1253 };
1254
1255 Roo.data.Field.prototype = {
1256     dateFormat: null,
1257     defaultValue: "",
1258     mapping: null,
1259     sortType : null,
1260     sortDir : "ASC"
1261 };/*
1262  * Based on:
1263  * Ext JS Library 1.1.1
1264  * Copyright(c) 2006-2007, Ext JS, LLC.
1265  *
1266  * Originally Released Under LGPL - original licence link has changed is not relivant.
1267  *
1268  * Fork - LGPL
1269  * <script type="text/javascript">
1270  */
1271  
1272 // Base class for reading structured data from a data source.  This class is intended to be
1273 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1274
1275 /**
1276  * @class Roo.data.DataReader
1277  * @abstract
1278  * Base class for reading structured data from a data source.  This class is intended to be
1279  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1280  */
1281
1282 Roo.data.DataReader = function(meta, recordType){
1283     
1284     this.meta = meta;
1285     
1286     this.recordType = recordType instanceof Array ? 
1287         Roo.data.Record.create(recordType) : recordType;
1288 };
1289
1290 Roo.data.DataReader.prototype = {
1291     
1292     
1293     readerType : 'Data',
1294      /**
1295      * Create an empty record
1296      * @param {Object} data (optional) - overlay some values
1297      * @return {Roo.data.Record} record created.
1298      */
1299     newRow :  function(d) {
1300         var da =  {};
1301         this.recordType.prototype.fields.each(function(c) {
1302             switch( c.type) {
1303                 case 'int' : da[c.name] = 0; break;
1304                 case 'date' : da[c.name] = new Date(); break;
1305                 case 'float' : da[c.name] = 0.0; break;
1306                 case 'boolean' : da[c.name] = false; break;
1307                 default : da[c.name] = ""; break;
1308             }
1309             
1310         });
1311         return new this.recordType(Roo.apply(da, d));
1312     }
1313     
1314     
1315 };/*
1316  * Based on:
1317  * Ext JS Library 1.1.1
1318  * Copyright(c) 2006-2007, Ext JS, LLC.
1319  *
1320  * Originally Released Under LGPL - original licence link has changed is not relivant.
1321  *
1322  * Fork - LGPL
1323  * <script type="text/javascript">
1324  */
1325
1326 /**
1327  * @class Roo.data.DataProxy
1328  * @extends Roo.util.Observable
1329  * @abstract
1330  * This class is an abstract base class for implementations which provide retrieval of
1331  * unformatted data objects.<br>
1332  * <p>
1333  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1334  * (of the appropriate type which knows how to parse the data object) to provide a block of
1335  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1336  * <p>
1337  * Custom implementations must implement the load method as described in
1338  * {@link Roo.data.HttpProxy#load}.
1339  */
1340 Roo.data.DataProxy = function(){
1341     this.addEvents({
1342         /**
1343          * @event beforeload
1344          * Fires before a network request is made to retrieve a data object.
1345          * @param {Object} This DataProxy object.
1346          * @param {Object} params The params parameter to the load function.
1347          */
1348         beforeload : true,
1349         /**
1350          * @event load
1351          * Fires before the load method's callback is called.
1352          * @param {Object} This DataProxy object.
1353          * @param {Object} o The data object.
1354          * @param {Object} arg The callback argument object passed to the load function.
1355          */
1356         load : true,
1357         /**
1358          * @event loadexception
1359          * Fires if an Exception occurs during data retrieval.
1360          * @param {Object} This DataProxy object.
1361          * @param {Object} o The data object.
1362          * @param {Object} arg The callback argument object passed to the load function.
1363          * @param {Object} e The Exception.
1364          */
1365         loadexception : true
1366     });
1367     Roo.data.DataProxy.superclass.constructor.call(this);
1368 };
1369
1370 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1371
1372     /**
1373      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1374      */
1375 /*
1376  * Based on:
1377  * Ext JS Library 1.1.1
1378  * Copyright(c) 2006-2007, Ext JS, LLC.
1379  *
1380  * Originally Released Under LGPL - original licence link has changed is not relivant.
1381  *
1382  * Fork - LGPL
1383  * <script type="text/javascript">
1384  */
1385 /**
1386  * @class Roo.data.MemoryProxy
1387  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1388  * to the Reader when its load method is called.
1389  * @constructor
1390  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1391  */
1392 Roo.data.MemoryProxy = function(data){
1393     if (data.data) {
1394         data = data.data;
1395     }
1396     Roo.data.MemoryProxy.superclass.constructor.call(this);
1397     this.data = data;
1398 };
1399
1400 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1401     
1402     /**
1403      * Load data from the requested source (in this case an in-memory
1404      * data object passed to the constructor), read the data object into
1405      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1406      * process that block using the passed callback.
1407      * @param {Object} params This parameter is not used by the MemoryProxy class.
1408      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1409      * object into a block of Roo.data.Records.
1410      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1411      * The function must be passed <ul>
1412      * <li>The Record block object</li>
1413      * <li>The "arg" argument from the load function</li>
1414      * <li>A boolean success indicator</li>
1415      * </ul>
1416      * @param {Object} scope The scope in which to call the callback
1417      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1418      */
1419     load : function(params, reader, callback, scope, arg){
1420         params = params || {};
1421         var result;
1422         try {
1423             result = reader.readRecords(params.data ? params.data :this.data);
1424         }catch(e){
1425             this.fireEvent("loadexception", this, arg, null, e);
1426             callback.call(scope, null, arg, false);
1427             return;
1428         }
1429         callback.call(scope, result, arg, true);
1430     },
1431     
1432     // private
1433     update : function(params, records){
1434         
1435     }
1436 });/*
1437  * Based on:
1438  * Ext JS Library 1.1.1
1439  * Copyright(c) 2006-2007, Ext JS, LLC.
1440  *
1441  * Originally Released Under LGPL - original licence link has changed is not relivant.
1442  *
1443  * Fork - LGPL
1444  * <script type="text/javascript">
1445  */
1446 /**
1447  * @class Roo.data.HttpProxy
1448  * @extends Roo.data.DataProxy
1449  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1450  * configured to reference a certain URL.<br><br>
1451  * <p>
1452  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1453  * from which the running page was served.<br><br>
1454  * <p>
1455  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1456  * <p>
1457  * Be aware that to enable the browser to parse an XML document, the server must set
1458  * the Content-Type header in the HTTP response to "text/xml".
1459  * @constructor
1460  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1461  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1462  * will be used to make the request.
1463  */
1464 Roo.data.HttpProxy = function(conn){
1465     Roo.data.HttpProxy.superclass.constructor.call(this);
1466     // is conn a conn config or a real conn?
1467     this.conn = conn;
1468     this.useAjax = !conn || !conn.events;
1469   
1470 };
1471
1472 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1473     // thse are take from connection...
1474     
1475     /**
1476      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1477      */
1478     /**
1479      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1480      * extra parameters to each request made by this object. (defaults to undefined)
1481      */
1482     /**
1483      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1484      *  to each request made by this object. (defaults to undefined)
1485      */
1486     /**
1487      * @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)
1488      */
1489     /**
1490      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1491      */
1492      /**
1493      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1494      * @type Boolean
1495      */
1496   
1497
1498     /**
1499      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1500      * @type Boolean
1501      */
1502     /**
1503      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1504      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1505      * a finer-grained basis than the DataProxy events.
1506      */
1507     getConnection : function(){
1508         return this.useAjax ? Roo.Ajax : this.conn;
1509     },
1510
1511     /**
1512      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1513      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1514      * process that block using the passed callback.
1515      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1516      * for the request to the remote server.
1517      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1518      * object into a block of Roo.data.Records.
1519      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1520      * The function must be passed <ul>
1521      * <li>The Record block object</li>
1522      * <li>The "arg" argument from the load function</li>
1523      * <li>A boolean success indicator</li>
1524      * </ul>
1525      * @param {Object} scope The scope in which to call the callback
1526      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1527      */
1528     load : function(params, reader, callback, scope, arg){
1529         if(this.fireEvent("beforeload", this, params) !== false){
1530             var  o = {
1531                 params : params || {},
1532                 request: {
1533                     callback : callback,
1534                     scope : scope,
1535                     arg : arg
1536                 },
1537                 reader: reader,
1538                 callback : this.loadResponse,
1539                 scope: this
1540             };
1541             if(this.useAjax){
1542                 Roo.applyIf(o, this.conn);
1543                 if(this.activeRequest){
1544                     Roo.Ajax.abort(this.activeRequest);
1545                 }
1546                 this.activeRequest = Roo.Ajax.request(o);
1547             }else{
1548                 this.conn.request(o);
1549             }
1550         }else{
1551             callback.call(scope||this, null, arg, false);
1552         }
1553     },
1554
1555     // private
1556     loadResponse : function(o, success, response){
1557         delete this.activeRequest;
1558         if(!success){
1559             this.fireEvent("loadexception", this, o, response);
1560             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1561             return;
1562         }
1563         var result;
1564         try {
1565             result = o.reader.read(response);
1566         }catch(e){
1567             o.success = false;
1568             o.raw = { errorMsg : response.responseText };
1569             this.fireEvent("loadexception", this, o, response, e);
1570             o.request.callback.call(o.request.scope, o, o.request.arg, false);
1571             return;
1572         }
1573         
1574         this.fireEvent("load", this, o, o.request.arg);
1575         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1576     },
1577
1578     // private
1579     update : function(dataSet){
1580
1581     },
1582
1583     // private
1584     updateResponse : function(dataSet){
1585
1586     }
1587 });/*
1588  * Based on:
1589  * Ext JS Library 1.1.1
1590  * Copyright(c) 2006-2007, Ext JS, LLC.
1591  *
1592  * Originally Released Under LGPL - original licence link has changed is not relivant.
1593  *
1594  * Fork - LGPL
1595  * <script type="text/javascript">
1596  */
1597
1598 /**
1599  * @class Roo.data.ScriptTagProxy
1600  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1601  * other than the originating domain of the running page.<br><br>
1602  * <p>
1603  * <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
1604  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1605  * <p>
1606  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1607  * source code that is used as the source inside a &lt;script> tag.<br><br>
1608  * <p>
1609  * In order for the browser to process the returned data, the server must wrap the data object
1610  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1611  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1612  * depending on whether the callback name was passed:
1613  * <p>
1614  * <pre><code>
1615 boolean scriptTag = false;
1616 String cb = request.getParameter("callback");
1617 if (cb != null) {
1618     scriptTag = true;
1619     response.setContentType("text/javascript");
1620 } else {
1621     response.setContentType("application/x-json");
1622 }
1623 Writer out = response.getWriter();
1624 if (scriptTag) {
1625     out.write(cb + "(");
1626 }
1627 out.print(dataBlock.toJsonString());
1628 if (scriptTag) {
1629     out.write(");");
1630 }
1631 </pre></code>
1632  *
1633  * @constructor
1634  * @param {Object} config A configuration object.
1635  */
1636 Roo.data.ScriptTagProxy = function(config){
1637     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1638     Roo.apply(this, config);
1639     this.head = document.getElementsByTagName("head")[0];
1640 };
1641
1642 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1643
1644 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1645     /**
1646      * @cfg {String} url The URL from which to request the data object.
1647      */
1648     /**
1649      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1650      */
1651     timeout : 30000,
1652     /**
1653      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1654      * the server the name of the callback function set up by the load call to process the returned data object.
1655      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1656      * javascript output which calls this named function passing the data object as its only parameter.
1657      */
1658     callbackParam : "callback",
1659     /**
1660      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1661      * name to the request.
1662      */
1663     nocache : true,
1664
1665     /**
1666      * Load data from the configured URL, read the data object into
1667      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1668      * process that block using the passed callback.
1669      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1670      * for the request to the remote server.
1671      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1672      * object into a block of Roo.data.Records.
1673      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1674      * The function must be passed <ul>
1675      * <li>The Record block object</li>
1676      * <li>The "arg" argument from the load function</li>
1677      * <li>A boolean success indicator</li>
1678      * </ul>
1679      * @param {Object} scope The scope in which to call the callback
1680      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1681      */
1682     load : function(params, reader, callback, scope, arg){
1683         if(this.fireEvent("beforeload", this, params) !== false){
1684
1685             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1686
1687             var url = this.url;
1688             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1689             if(this.nocache){
1690                 url += "&_dc=" + (new Date().getTime());
1691             }
1692             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1693             var trans = {
1694                 id : transId,
1695                 cb : "stcCallback"+transId,
1696                 scriptId : "stcScript"+transId,
1697                 params : params,
1698                 arg : arg,
1699                 url : url,
1700                 callback : callback,
1701                 scope : scope,
1702                 reader : reader
1703             };
1704             var conn = this;
1705
1706             window[trans.cb] = function(o){
1707                 conn.handleResponse(o, trans);
1708             };
1709
1710             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1711
1712             if(this.autoAbort !== false){
1713                 this.abort();
1714             }
1715
1716             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1717
1718             var script = document.createElement("script");
1719             script.setAttribute("src", url);
1720             script.setAttribute("type", "text/javascript");
1721             script.setAttribute("id", trans.scriptId);
1722             this.head.appendChild(script);
1723
1724             this.trans = trans;
1725         }else{
1726             callback.call(scope||this, null, arg, false);
1727         }
1728     },
1729
1730     // private
1731     isLoading : function(){
1732         return this.trans ? true : false;
1733     },
1734
1735     /**
1736      * Abort the current server request.
1737      */
1738     abort : function(){
1739         if(this.isLoading()){
1740             this.destroyTrans(this.trans);
1741         }
1742     },
1743
1744     // private
1745     destroyTrans : function(trans, isLoaded){
1746         this.head.removeChild(document.getElementById(trans.scriptId));
1747         clearTimeout(trans.timeoutId);
1748         if(isLoaded){
1749             window[trans.cb] = undefined;
1750             try{
1751                 delete window[trans.cb];
1752             }catch(e){}
1753         }else{
1754             // if hasn't been loaded, wait for load to remove it to prevent script error
1755             window[trans.cb] = function(){
1756                 window[trans.cb] = undefined;
1757                 try{
1758                     delete window[trans.cb];
1759                 }catch(e){}
1760             };
1761         }
1762     },
1763
1764     // private
1765     handleResponse : function(o, trans){
1766         this.trans = false;
1767         this.destroyTrans(trans, true);
1768         var result;
1769         try {
1770             result = trans.reader.readRecords(o);
1771         }catch(e){
1772             this.fireEvent("loadexception", this, o, trans.arg, e);
1773             trans.callback.call(trans.scope||window, null, trans.arg, false);
1774             return;
1775         }
1776         this.fireEvent("load", this, o, trans.arg);
1777         trans.callback.call(trans.scope||window, result, trans.arg, true);
1778     },
1779
1780     // private
1781     handleFailure : function(trans){
1782         this.trans = false;
1783         this.destroyTrans(trans, false);
1784         this.fireEvent("loadexception", this, null, trans.arg);
1785         trans.callback.call(trans.scope||window, null, trans.arg, false);
1786     }
1787 });/*
1788  * Based on:
1789  * Ext JS Library 1.1.1
1790  * Copyright(c) 2006-2007, Ext JS, LLC.
1791  *
1792  * Originally Released Under LGPL - original licence link has changed is not relivant.
1793  *
1794  * Fork - LGPL
1795  * <script type="text/javascript">
1796  */
1797
1798 /**
1799  * @class Roo.data.JsonReader
1800  * @extends Roo.data.DataReader
1801  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1802  * based on mappings in a provided Roo.data.Record constructor.
1803  * 
1804  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1805  * in the reply previously. 
1806  * 
1807  * <p>
1808  * Example code:
1809  * <pre><code>
1810 var RecordDef = Roo.data.Record.create([
1811     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1812     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1813 ]);
1814 var myReader = new Roo.data.JsonReader({
1815     totalProperty: "results",    // The property which contains the total dataset size (optional)
1816     root: "rows",                // The property which contains an Array of row objects
1817     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1818 }, RecordDef);
1819 </code></pre>
1820  * <p>
1821  * This would consume a JSON file like this:
1822  * <pre><code>
1823 { 'results': 2, 'rows': [
1824     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1825     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1826 }
1827 </code></pre>
1828  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1829  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1830  * paged from the remote server.
1831  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1832  * @cfg {String} root name of the property which contains the Array of row objects.
1833  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1834  * @cfg {Array} fields Array of field definition objects
1835  * @constructor
1836  * Create a new JsonReader
1837  * @param {Object} meta Metadata configuration options
1838  * @param {Object} recordType Either an Array of field definition objects,
1839  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1840  */
1841 Roo.data.JsonReader = function(meta, recordType){
1842     
1843     meta = meta || {};
1844     // set some defaults:
1845     Roo.applyIf(meta, {
1846         totalProperty: 'total',
1847         successProperty : 'success',
1848         root : 'data',
1849         id : 'id'
1850     });
1851     
1852     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1853 };
1854 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1855     
1856     readerType : 'Json',
1857     
1858     /**
1859      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1860      * Used by Store query builder to append _requestMeta to params.
1861      * 
1862      */
1863     metaFromRemote : false,
1864     /**
1865      * This method is only used by a DataProxy which has retrieved data from a remote server.
1866      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1867      * @return {Object} data A data block which is used by an Roo.data.Store object as
1868      * a cache of Roo.data.Records.
1869      */
1870     read : function(response){
1871         var json = response.responseText;
1872        
1873         var o = /* eval:var:o */ eval("("+json+")");
1874         if(!o) {
1875             throw {message: "JsonReader.read: Json object not found"};
1876         }
1877         
1878         if(o.metaData){
1879             
1880             delete this.ef;
1881             this.metaFromRemote = true;
1882             this.meta = o.metaData;
1883             this.recordType = Roo.data.Record.create(o.metaData.fields);
1884             this.onMetaChange(this.meta, this.recordType, o);
1885         }
1886         return this.readRecords(o);
1887     },
1888
1889     // private function a store will implement
1890     onMetaChange : function(meta, recordType, o){
1891
1892     },
1893
1894     /**
1895          * @ignore
1896          */
1897     simpleAccess: function(obj, subsc) {
1898         return obj[subsc];
1899     },
1900
1901         /**
1902          * @ignore
1903          */
1904     getJsonAccessor: function(){
1905         var re = /[\[\.]/;
1906         return function(expr) {
1907             try {
1908                 return(re.test(expr))
1909                     ? new Function("obj", "return obj." + expr)
1910                     : function(obj){
1911                         return obj[expr];
1912                     };
1913             } catch(e){}
1914             return Roo.emptyFn;
1915         };
1916     }(),
1917
1918     /**
1919      * Create a data block containing Roo.data.Records from an XML document.
1920      * @param {Object} o An object which contains an Array of row objects in the property specified
1921      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1922      * which contains the total size of the dataset.
1923      * @return {Object} data A data block which is used by an Roo.data.Store object as
1924      * a cache of Roo.data.Records.
1925      */
1926     readRecords : function(o){
1927         /**
1928          * After any data loads, the raw JSON data is available for further custom processing.
1929          * @type Object
1930          */
1931         this.o = o;
1932         var s = this.meta, Record = this.recordType,
1933             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1934
1935 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1936         if (!this.ef) {
1937             if(s.totalProperty) {
1938                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1939                 }
1940                 if(s.successProperty) {
1941                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1942                 }
1943                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1944                 if (s.id) {
1945                         var g = this.getJsonAccessor(s.id);
1946                         this.getId = function(rec) {
1947                                 var r = g(rec);  
1948                                 return (r === undefined || r === "") ? null : r;
1949                         };
1950                 } else {
1951                         this.getId = function(){return null;};
1952                 }
1953             this.ef = [];
1954             for(var jj = 0; jj < fl; jj++){
1955                 f = fi[jj];
1956                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1957                 this.ef[jj] = this.getJsonAccessor(map);
1958             }
1959         }
1960
1961         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1962         if(s.totalProperty){
1963             var vt = parseInt(this.getTotal(o), 10);
1964             if(!isNaN(vt)){
1965                 totalRecords = vt;
1966             }
1967         }
1968         if(s.successProperty){
1969             var vs = this.getSuccess(o);
1970             if(vs === false || vs === 'false'){
1971                 success = false;
1972             }
1973         }
1974         var records = [];
1975         for(var i = 0; i < c; i++){
1976             var n = root[i];
1977             var values = {};
1978             var id = this.getId(n);
1979             for(var j = 0; j < fl; j++){
1980                 f = fi[j];
1981                                 var v = this.ef[j](n);
1982                                 if (!f.convert) {
1983                                         Roo.log('missing convert for ' + f.name);
1984                                         Roo.log(f);
1985                                         continue;
1986                                 }
1987                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1988             }
1989                         if (!Record) {
1990                                 return {
1991                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
1992                                         success : false,
1993                                         records : [],
1994                                         totalRecords : 0
1995                                 };
1996                         }
1997             var record = new Record(values, id);
1998             record.json = n;
1999             records[i] = record;
2000         }
2001         return {
2002             raw : o,
2003             success : success,
2004             records : records,
2005             totalRecords : totalRecords
2006         };
2007     },
2008     // used when loading children.. @see loadDataFromChildren
2009     toLoadData: function(rec)
2010     {
2011         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2012         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2013         return { data : data, total : data.length };
2014         
2015     }
2016 });/*
2017  * Based on:
2018  * Ext JS Library 1.1.1
2019  * Copyright(c) 2006-2007, Ext JS, LLC.
2020  *
2021  * Originally Released Under LGPL - original licence link has changed is not relivant.
2022  *
2023  * Fork - LGPL
2024  * <script type="text/javascript">
2025  */
2026
2027 /**
2028  * @class Roo.data.XmlReader
2029  * @extends Roo.data.DataReader
2030  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2031  * based on mappings in a provided Roo.data.Record constructor.<br><br>
2032  * <p>
2033  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2034  * header in the HTTP response must be set to "text/xml".</em>
2035  * <p>
2036  * Example code:
2037  * <pre><code>
2038 var RecordDef = Roo.data.Record.create([
2039    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2040    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2041 ]);
2042 var myReader = new Roo.data.XmlReader({
2043    totalRecords: "results", // The element which contains the total dataset size (optional)
2044    record: "row",           // The repeated element which contains row information
2045    id: "id"                 // The element within the row that provides an ID for the record (optional)
2046 }, RecordDef);
2047 </code></pre>
2048  * <p>
2049  * This would consume an XML file like this:
2050  * <pre><code>
2051 &lt;?xml?>
2052 &lt;dataset>
2053  &lt;results>2&lt;/results>
2054  &lt;row>
2055    &lt;id>1&lt;/id>
2056    &lt;name>Bill&lt;/name>
2057    &lt;occupation>Gardener&lt;/occupation>
2058  &lt;/row>
2059  &lt;row>
2060    &lt;id>2&lt;/id>
2061    &lt;name>Ben&lt;/name>
2062    &lt;occupation>Horticulturalist&lt;/occupation>
2063  &lt;/row>
2064 &lt;/dataset>
2065 </code></pre>
2066  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2067  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2068  * paged from the remote server.
2069  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2070  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2071  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2072  * a record identifier value.
2073  * @constructor
2074  * Create a new XmlReader
2075  * @param {Object} meta Metadata configuration options
2076  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2077  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2078  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2079  */
2080 Roo.data.XmlReader = function(meta, recordType){
2081     meta = meta || {};
2082     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2083 };
2084 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2085     
2086     readerType : 'Xml',
2087     
2088     /**
2089      * This method is only used by a DataProxy which has retrieved data from a remote server.
2090          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2091          * to contain a method called 'responseXML' that returns an XML document object.
2092      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2093      * a cache of Roo.data.Records.
2094      */
2095     read : function(response){
2096         var doc = response.responseXML;
2097         if(!doc) {
2098             throw {message: "XmlReader.read: XML Document not available"};
2099         }
2100         return this.readRecords(doc);
2101     },
2102
2103     /**
2104      * Create a data block containing Roo.data.Records from an XML document.
2105          * @param {Object} doc A parsed XML document.
2106      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2107      * a cache of Roo.data.Records.
2108      */
2109     readRecords : function(doc){
2110         /**
2111          * After any data loads/reads, the raw XML Document is available for further custom processing.
2112          * @type XMLDocument
2113          */
2114         this.xmlData = doc;
2115         var root = doc.documentElement || doc;
2116         var q = Roo.DomQuery;
2117         var recordType = this.recordType, fields = recordType.prototype.fields;
2118         var sid = this.meta.id;
2119         var totalRecords = 0, success = true;
2120         if(this.meta.totalRecords){
2121             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2122         }
2123         
2124         if(this.meta.success){
2125             var sv = q.selectValue(this.meta.success, root, true);
2126             success = sv !== false && sv !== 'false';
2127         }
2128         var records = [];
2129         var ns = q.select(this.meta.record, root);
2130         for(var i = 0, len = ns.length; i < len; i++) {
2131                 var n = ns[i];
2132                 var values = {};
2133                 var id = sid ? q.selectValue(sid, n) : undefined;
2134                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2135                     var f = fields.items[j];
2136                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2137                     v = f.convert(v);
2138                     values[f.name] = v;
2139                 }
2140                 var record = new recordType(values, id);
2141                 record.node = n;
2142                 records[records.length] = record;
2143             }
2144
2145             return {
2146                 success : success,
2147                 records : records,
2148                 totalRecords : totalRecords || records.length
2149             };
2150     }
2151 });/*
2152  * Based on:
2153  * Ext JS Library 1.1.1
2154  * Copyright(c) 2006-2007, Ext JS, LLC.
2155  *
2156  * Originally Released Under LGPL - original licence link has changed is not relivant.
2157  *
2158  * Fork - LGPL
2159  * <script type="text/javascript">
2160  */
2161
2162 /**
2163  * @class Roo.data.ArrayReader
2164  * @extends Roo.data.DataReader
2165  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2166  * Each element of that Array represents a row of data fields. The
2167  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2168  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2169  * <p>
2170  * Example code:.
2171  * <pre><code>
2172 var RecordDef = Roo.data.Record.create([
2173     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2174     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2175 ]);
2176 var myReader = new Roo.data.ArrayReader({
2177     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2178 }, RecordDef);
2179 </code></pre>
2180  * <p>
2181  * This would consume an Array like this:
2182  * <pre><code>
2183 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2184   </code></pre>
2185  
2186  * @constructor
2187  * Create a new JsonReader
2188  * @param {Object} meta Metadata configuration options.
2189  * @param {Object|Array} recordType Either an Array of field definition objects
2190  * 
2191  * @cfg {Array} fields Array of field definition objects
2192  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2193  * as specified to {@link Roo.data.Record#create},
2194  * or an {@link Roo.data.Record} object
2195  *
2196  * 
2197  * created using {@link Roo.data.Record#create}.
2198  */
2199 Roo.data.ArrayReader = function(meta, recordType)
2200 {    
2201     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2202 };
2203
2204 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2205     
2206       /**
2207      * Create a data block containing Roo.data.Records from an XML document.
2208      * @param {Object} o An Array of row objects which represents the dataset.
2209      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2210      * a cache of Roo.data.Records.
2211      */
2212     readRecords : function(o)
2213     {
2214         var sid = this.meta ? this.meta.id : null;
2215         var recordType = this.recordType, fields = recordType.prototype.fields;
2216         var records = [];
2217         var root = o;
2218         for(var i = 0; i < root.length; i++){
2219             var n = root[i];
2220             var values = {};
2221             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2222             for(var j = 0, jlen = fields.length; j < jlen; j++){
2223                 var f = fields.items[j];
2224                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2225                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2226                 v = f.convert(v);
2227                 values[f.name] = v;
2228             }
2229             var record = new recordType(values, id);
2230             record.json = n;
2231             records[records.length] = record;
2232         }
2233         return {
2234             records : records,
2235             totalRecords : records.length
2236         };
2237     },
2238     // used when loading children.. @see loadDataFromChildren
2239     toLoadData: function(rec)
2240     {
2241         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2242         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2243         
2244     }
2245     
2246     
2247 });/*
2248  * Based on:
2249  * Ext JS Library 1.1.1
2250  * Copyright(c) 2006-2007, Ext JS, LLC.
2251  *
2252  * Originally Released Under LGPL - original licence link has changed is not relivant.
2253  *
2254  * Fork - LGPL
2255  * <script type="text/javascript">
2256  */
2257
2258
2259 /**
2260  * @class Roo.data.Tree
2261  * @extends Roo.util.Observable
2262  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2263  * in the tree have most standard DOM functionality.
2264  * @constructor
2265  * @param {Node} root (optional) The root node
2266  */
2267 Roo.data.Tree = function(root){
2268    this.nodeHash = {};
2269    /**
2270     * The root node for this tree
2271     * @type Node
2272     */
2273    this.root = null;
2274    if(root){
2275        this.setRootNode(root);
2276    }
2277    this.addEvents({
2278        /**
2279         * @event append
2280         * Fires when a new child node is appended to a node in this tree.
2281         * @param {Tree} tree The owner tree
2282         * @param {Node} parent The parent node
2283         * @param {Node} node The newly appended node
2284         * @param {Number} index The index of the newly appended node
2285         */
2286        "append" : true,
2287        /**
2288         * @event remove
2289         * Fires when a child node is removed from a node in this tree.
2290         * @param {Tree} tree The owner tree
2291         * @param {Node} parent The parent node
2292         * @param {Node} node The child node removed
2293         */
2294        "remove" : true,
2295        /**
2296         * @event move
2297         * Fires when a node is moved to a new location in the tree
2298         * @param {Tree} tree The owner tree
2299         * @param {Node} node The node moved
2300         * @param {Node} oldParent The old parent of this node
2301         * @param {Node} newParent The new parent of this node
2302         * @param {Number} index The index it was moved to
2303         */
2304        "move" : true,
2305        /**
2306         * @event insert
2307         * Fires when a new child node is inserted in a node in this tree.
2308         * @param {Tree} tree The owner tree
2309         * @param {Node} parent The parent node
2310         * @param {Node} node The child node inserted
2311         * @param {Node} refNode The child node the node was inserted before
2312         */
2313        "insert" : true,
2314        /**
2315         * @event beforeappend
2316         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2317         * @param {Tree} tree The owner tree
2318         * @param {Node} parent The parent node
2319         * @param {Node} node The child node to be appended
2320         */
2321        "beforeappend" : true,
2322        /**
2323         * @event beforeremove
2324         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2325         * @param {Tree} tree The owner tree
2326         * @param {Node} parent The parent node
2327         * @param {Node} node The child node to be removed
2328         */
2329        "beforeremove" : true,
2330        /**
2331         * @event beforemove
2332         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2333         * @param {Tree} tree The owner tree
2334         * @param {Node} node The node being moved
2335         * @param {Node} oldParent The parent of the node
2336         * @param {Node} newParent The new parent the node is moving to
2337         * @param {Number} index The index it is being moved to
2338         */
2339        "beforemove" : true,
2340        /**
2341         * @event beforeinsert
2342         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2343         * @param {Tree} tree The owner tree
2344         * @param {Node} parent The parent node
2345         * @param {Node} node The child node to be inserted
2346         * @param {Node} refNode The child node the node is being inserted before
2347         */
2348        "beforeinsert" : true
2349    });
2350
2351     Roo.data.Tree.superclass.constructor.call(this);
2352 };
2353
2354 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2355     pathSeparator: "/",
2356
2357     proxyNodeEvent : function(){
2358         return this.fireEvent.apply(this, arguments);
2359     },
2360
2361     /**
2362      * Returns the root node for this tree.
2363      * @return {Node}
2364      */
2365     getRootNode : function(){
2366         return this.root;
2367     },
2368
2369     /**
2370      * Sets the root node for this tree.
2371      * @param {Node} node
2372      * @return {Node}
2373      */
2374     setRootNode : function(node){
2375         this.root = node;
2376         node.ownerTree = this;
2377         node.isRoot = true;
2378         this.registerNode(node);
2379         return node;
2380     },
2381
2382     /**
2383      * Gets a node in this tree by its id.
2384      * @param {String} id
2385      * @return {Node}
2386      */
2387     getNodeById : function(id){
2388         return this.nodeHash[id];
2389     },
2390
2391     registerNode : function(node){
2392         this.nodeHash[node.id] = node;
2393     },
2394
2395     unregisterNode : function(node){
2396         delete this.nodeHash[node.id];
2397     },
2398
2399     toString : function(){
2400         return "[Tree"+(this.id?" "+this.id:"")+"]";
2401     }
2402 });
2403
2404 /**
2405  * @class Roo.data.Node
2406  * @extends Roo.util.Observable
2407  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2408  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2409  * @constructor
2410  * @param {Object} attributes The attributes/config for the node
2411  */
2412 Roo.data.Node = function(attributes){
2413     /**
2414      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2415      * @type {Object}
2416      */
2417     this.attributes = attributes || {};
2418     this.leaf = this.attributes.leaf;
2419     /**
2420      * The node id. @type String
2421      */
2422     this.id = this.attributes.id;
2423     if(!this.id){
2424         this.id = Roo.id(null, "ynode-");
2425         this.attributes.id = this.id;
2426     }
2427      
2428     
2429     /**
2430      * All child nodes of this node. @type Array
2431      */
2432     this.childNodes = [];
2433     if(!this.childNodes.indexOf){ // indexOf is a must
2434         this.childNodes.indexOf = function(o){
2435             for(var i = 0, len = this.length; i < len; i++){
2436                 if(this[i] == o) {
2437                     return i;
2438                 }
2439             }
2440             return -1;
2441         };
2442     }
2443     /**
2444      * The parent node for this node. @type Node
2445      */
2446     this.parentNode = null;
2447     /**
2448      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2449      */
2450     this.firstChild = null;
2451     /**
2452      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2453      */
2454     this.lastChild = null;
2455     /**
2456      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2457      */
2458     this.previousSibling = null;
2459     /**
2460      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2461      */
2462     this.nextSibling = null;
2463
2464     this.addEvents({
2465        /**
2466         * @event append
2467         * Fires when a new child node is appended
2468         * @param {Tree} tree The owner tree
2469         * @param {Node} this This node
2470         * @param {Node} node The newly appended node
2471         * @param {Number} index The index of the newly appended node
2472         */
2473        "append" : true,
2474        /**
2475         * @event remove
2476         * Fires when a child node is removed
2477         * @param {Tree} tree The owner tree
2478         * @param {Node} this This node
2479         * @param {Node} node The removed node
2480         */
2481        "remove" : true,
2482        /**
2483         * @event move
2484         * Fires when this node is moved to a new location in the tree
2485         * @param {Tree} tree The owner tree
2486         * @param {Node} this This node
2487         * @param {Node} oldParent The old parent of this node
2488         * @param {Node} newParent The new parent of this node
2489         * @param {Number} index The index it was moved to
2490         */
2491        "move" : true,
2492        /**
2493         * @event insert
2494         * Fires when a new child node is inserted.
2495         * @param {Tree} tree The owner tree
2496         * @param {Node} this This node
2497         * @param {Node} node The child node inserted
2498         * @param {Node} refNode The child node the node was inserted before
2499         */
2500        "insert" : true,
2501        /**
2502         * @event beforeappend
2503         * Fires before a new child is appended, return false to cancel the append.
2504         * @param {Tree} tree The owner tree
2505         * @param {Node} this This node
2506         * @param {Node} node The child node to be appended
2507         */
2508        "beforeappend" : true,
2509        /**
2510         * @event beforeremove
2511         * Fires before a child is removed, return false to cancel the remove.
2512         * @param {Tree} tree The owner tree
2513         * @param {Node} this This node
2514         * @param {Node} node The child node to be removed
2515         */
2516        "beforeremove" : true,
2517        /**
2518         * @event beforemove
2519         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2520         * @param {Tree} tree The owner tree
2521         * @param {Node} this This node
2522         * @param {Node} oldParent The parent of this node
2523         * @param {Node} newParent The new parent this node is moving to
2524         * @param {Number} index The index it is being moved to
2525         */
2526        "beforemove" : true,
2527        /**
2528         * @event beforeinsert
2529         * Fires before a new child is inserted, return false to cancel the insert.
2530         * @param {Tree} tree The owner tree
2531         * @param {Node} this This node
2532         * @param {Node} node The child node to be inserted
2533         * @param {Node} refNode The child node the node is being inserted before
2534         */
2535        "beforeinsert" : true
2536    });
2537     this.listeners = this.attributes.listeners;
2538     Roo.data.Node.superclass.constructor.call(this);
2539 };
2540
2541 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2542     fireEvent : function(evtName){
2543         // first do standard event for this node
2544         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2545             return false;
2546         }
2547         // then bubble it up to the tree if the event wasn't cancelled
2548         var ot = this.getOwnerTree();
2549         if(ot){
2550             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2551                 return false;
2552             }
2553         }
2554         return true;
2555     },
2556
2557     /**
2558      * Returns true if this node is a leaf
2559      * @return {Boolean}
2560      */
2561     isLeaf : function(){
2562         return this.leaf === true;
2563     },
2564
2565     // private
2566     setFirstChild : function(node){
2567         this.firstChild = node;
2568     },
2569
2570     //private
2571     setLastChild : function(node){
2572         this.lastChild = node;
2573     },
2574
2575
2576     /**
2577      * Returns true if this node is the last child of its parent
2578      * @return {Boolean}
2579      */
2580     isLast : function(){
2581        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2582     },
2583
2584     /**
2585      * Returns true if this node is the first child of its parent
2586      * @return {Boolean}
2587      */
2588     isFirst : function(){
2589        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2590     },
2591
2592     hasChildNodes : function(){
2593         return !this.isLeaf() && this.childNodes.length > 0;
2594     },
2595
2596     /**
2597      * Insert node(s) as the last child node of this node.
2598      * @param {Node/Array} node The node or Array of nodes to append
2599      * @return {Node} The appended node if single append, or null if an array was passed
2600      */
2601     appendChild : function(node){
2602         var multi = false;
2603         if(node instanceof Array){
2604             multi = node;
2605         }else if(arguments.length > 1){
2606             multi = arguments;
2607         }
2608         
2609         // if passed an array or multiple args do them one by one
2610         if(multi){
2611             for(var i = 0, len = multi.length; i < len; i++) {
2612                 this.appendChild(multi[i]);
2613             }
2614         }else{
2615             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2616                 return false;
2617             }
2618             var index = this.childNodes.length;
2619             var oldParent = node.parentNode;
2620             // it's a move, make sure we move it cleanly
2621             if(oldParent){
2622                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2623                     return false;
2624                 }
2625                 oldParent.removeChild(node);
2626             }
2627             
2628             index = this.childNodes.length;
2629             if(index == 0){
2630                 this.setFirstChild(node);
2631             }
2632             this.childNodes.push(node);
2633             node.parentNode = this;
2634             var ps = this.childNodes[index-1];
2635             if(ps){
2636                 node.previousSibling = ps;
2637                 ps.nextSibling = node;
2638             }else{
2639                 node.previousSibling = null;
2640             }
2641             node.nextSibling = null;
2642             this.setLastChild(node);
2643             node.setOwnerTree(this.getOwnerTree());
2644             this.fireEvent("append", this.ownerTree, this, node, index);
2645             if(this.ownerTree) {
2646                 this.ownerTree.fireEvent("appendnode", this, node, index);
2647             }
2648             if(oldParent){
2649                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2650             }
2651             return node;
2652         }
2653     },
2654
2655     /**
2656      * Removes a child node from this node.
2657      * @param {Node} node The node to remove
2658      * @return {Node} The removed node
2659      */
2660     removeChild : function(node){
2661         var index = this.childNodes.indexOf(node);
2662         if(index == -1){
2663             return false;
2664         }
2665         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2666             return false;
2667         }
2668
2669         // remove it from childNodes collection
2670         this.childNodes.splice(index, 1);
2671
2672         // update siblings
2673         if(node.previousSibling){
2674             node.previousSibling.nextSibling = node.nextSibling;
2675         }
2676         if(node.nextSibling){
2677             node.nextSibling.previousSibling = node.previousSibling;
2678         }
2679
2680         // update child refs
2681         if(this.firstChild == node){
2682             this.setFirstChild(node.nextSibling);
2683         }
2684         if(this.lastChild == node){
2685             this.setLastChild(node.previousSibling);
2686         }
2687
2688         node.setOwnerTree(null);
2689         // clear any references from the node
2690         node.parentNode = null;
2691         node.previousSibling = null;
2692         node.nextSibling = null;
2693         this.fireEvent("remove", this.ownerTree, this, node);
2694         return node;
2695     },
2696
2697     /**
2698      * Inserts the first node before the second node in this nodes childNodes collection.
2699      * @param {Node} node The node to insert
2700      * @param {Node} refNode The node to insert before (if null the node is appended)
2701      * @return {Node} The inserted node
2702      */
2703     insertBefore : function(node, refNode){
2704         if(!refNode){ // like standard Dom, refNode can be null for append
2705             return this.appendChild(node);
2706         }
2707         // nothing to do
2708         if(node == refNode){
2709             return false;
2710         }
2711
2712         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2713             return false;
2714         }
2715         var index = this.childNodes.indexOf(refNode);
2716         var oldParent = node.parentNode;
2717         var refIndex = index;
2718
2719         // when moving internally, indexes will change after remove
2720         if(oldParent == this && this.childNodes.indexOf(node) < index){
2721             refIndex--;
2722         }
2723
2724         // it's a move, make sure we move it cleanly
2725         if(oldParent){
2726             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2727                 return false;
2728             }
2729             oldParent.removeChild(node);
2730         }
2731         if(refIndex == 0){
2732             this.setFirstChild(node);
2733         }
2734         this.childNodes.splice(refIndex, 0, node);
2735         node.parentNode = this;
2736         var ps = this.childNodes[refIndex-1];
2737         if(ps){
2738             node.previousSibling = ps;
2739             ps.nextSibling = node;
2740         }else{
2741             node.previousSibling = null;
2742         }
2743         node.nextSibling = refNode;
2744         refNode.previousSibling = node;
2745         node.setOwnerTree(this.getOwnerTree());
2746         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2747         if(oldParent){
2748             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2749         }
2750         return node;
2751     },
2752
2753     /**
2754      * Returns the child node at the specified index.
2755      * @param {Number} index
2756      * @return {Node}
2757      */
2758     item : function(index){
2759         return this.childNodes[index];
2760     },
2761
2762     /**
2763      * Replaces one child node in this node with another.
2764      * @param {Node} newChild The replacement node
2765      * @param {Node} oldChild The node to replace
2766      * @return {Node} The replaced node
2767      */
2768     replaceChild : function(newChild, oldChild){
2769         this.insertBefore(newChild, oldChild);
2770         this.removeChild(oldChild);
2771         return oldChild;
2772     },
2773
2774     /**
2775      * Returns the index of a child node
2776      * @param {Node} node
2777      * @return {Number} The index of the node or -1 if it was not found
2778      */
2779     indexOf : function(child){
2780         return this.childNodes.indexOf(child);
2781     },
2782
2783     /**
2784      * Returns the tree this node is in.
2785      * @return {Tree}
2786      */
2787     getOwnerTree : function(){
2788         // if it doesn't have one, look for one
2789         if(!this.ownerTree){
2790             var p = this;
2791             while(p){
2792                 if(p.ownerTree){
2793                     this.ownerTree = p.ownerTree;
2794                     break;
2795                 }
2796                 p = p.parentNode;
2797             }
2798         }
2799         return this.ownerTree;
2800     },
2801
2802     /**
2803      * Returns depth of this node (the root node has a depth of 0)
2804      * @return {Number}
2805      */
2806     getDepth : function(){
2807         var depth = 0;
2808         var p = this;
2809         while(p.parentNode){
2810             ++depth;
2811             p = p.parentNode;
2812         }
2813         return depth;
2814     },
2815
2816     // private
2817     setOwnerTree : function(tree){
2818         // if it's move, we need to update everyone
2819         if(tree != this.ownerTree){
2820             if(this.ownerTree){
2821                 this.ownerTree.unregisterNode(this);
2822             }
2823             this.ownerTree = tree;
2824             var cs = this.childNodes;
2825             for(var i = 0, len = cs.length; i < len; i++) {
2826                 cs[i].setOwnerTree(tree);
2827             }
2828             if(tree){
2829                 tree.registerNode(this);
2830             }
2831         }
2832     },
2833
2834     /**
2835      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2836      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2837      * @return {String} The path
2838      */
2839     getPath : function(attr){
2840         attr = attr || "id";
2841         var p = this.parentNode;
2842         var b = [this.attributes[attr]];
2843         while(p){
2844             b.unshift(p.attributes[attr]);
2845             p = p.parentNode;
2846         }
2847         var sep = this.getOwnerTree().pathSeparator;
2848         return sep + b.join(sep);
2849     },
2850
2851     /**
2852      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2853      * function call will be the scope provided or the current node. The arguments to the function
2854      * will be the args provided or the current node. If the function returns false at any point,
2855      * the bubble is stopped.
2856      * @param {Function} fn The function to call
2857      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2858      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2859      */
2860     bubble : function(fn, scope, args){
2861         var p = this;
2862         while(p){
2863             if(fn.call(scope || p, args || p) === false){
2864                 break;
2865             }
2866             p = p.parentNode;
2867         }
2868     },
2869
2870     /**
2871      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2872      * function call will be the scope provided or the current node. The arguments to the function
2873      * will be the args provided or the current node. If the function returns false at any point,
2874      * the cascade is stopped on that branch.
2875      * @param {Function} fn The function to call
2876      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2877      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2878      */
2879     cascade : function(fn, scope, args){
2880         if(fn.call(scope || this, args || this) !== false){
2881             var cs = this.childNodes;
2882             for(var i = 0, len = cs.length; i < len; i++) {
2883                 cs[i].cascade(fn, scope, args);
2884             }
2885         }
2886     },
2887
2888     /**
2889      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2890      * function call will be the scope provided or the current node. The arguments to the function
2891      * will be the args provided or the current node. If the function returns false at any point,
2892      * the iteration stops.
2893      * @param {Function} fn The function to call
2894      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2895      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2896      */
2897     eachChild : function(fn, scope, args){
2898         var cs = this.childNodes;
2899         for(var i = 0, len = cs.length; i < len; i++) {
2900                 if(fn.call(scope || this, args || cs[i]) === false){
2901                     break;
2902                 }
2903         }
2904     },
2905
2906     /**
2907      * Finds the first child that has the attribute with the specified value.
2908      * @param {String} attribute The attribute name
2909      * @param {Mixed} value The value to search for
2910      * @return {Node} The found child or null if none was found
2911      */
2912     findChild : function(attribute, value){
2913         var cs = this.childNodes;
2914         for(var i = 0, len = cs.length; i < len; i++) {
2915                 if(cs[i].attributes[attribute] == value){
2916                     return cs[i];
2917                 }
2918         }
2919         return null;
2920     },
2921
2922     /**
2923      * Finds the first child by a custom function. The child matches if the function passed
2924      * returns true.
2925      * @param {Function} fn
2926      * @param {Object} scope (optional)
2927      * @return {Node} The found child or null if none was found
2928      */
2929     findChildBy : function(fn, scope){
2930         var cs = this.childNodes;
2931         for(var i = 0, len = cs.length; i < len; i++) {
2932                 if(fn.call(scope||cs[i], cs[i]) === true){
2933                     return cs[i];
2934                 }
2935         }
2936         return null;
2937     },
2938
2939     /**
2940      * Sorts this nodes children using the supplied sort function
2941      * @param {Function} fn
2942      * @param {Object} scope (optional)
2943      */
2944     sort : function(fn, scope){
2945         var cs = this.childNodes;
2946         var len = cs.length;
2947         if(len > 0){
2948             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2949             cs.sort(sortFn);
2950             for(var i = 0; i < len; i++){
2951                 var n = cs[i];
2952                 n.previousSibling = cs[i-1];
2953                 n.nextSibling = cs[i+1];
2954                 if(i == 0){
2955                     this.setFirstChild(n);
2956                 }
2957                 if(i == len-1){
2958                     this.setLastChild(n);
2959                 }
2960             }
2961         }
2962     },
2963
2964     /**
2965      * Returns true if this node is an ancestor (at any point) of the passed node.
2966      * @param {Node} node
2967      * @return {Boolean}
2968      */
2969     contains : function(node){
2970         return node.isAncestor(this);
2971     },
2972
2973     /**
2974      * Returns true if the passed node is an ancestor (at any point) of this node.
2975      * @param {Node} node
2976      * @return {Boolean}
2977      */
2978     isAncestor : function(node){
2979         var p = this.parentNode;
2980         while(p){
2981             if(p == node){
2982                 return true;
2983             }
2984             p = p.parentNode;
2985         }
2986         return false;
2987     },
2988
2989     toString : function(){
2990         return "[Node"+(this.id?" "+this.id:"")+"]";
2991     }
2992 });/*
2993  * Based on:
2994  * Ext JS Library 1.1.1
2995  * Copyright(c) 2006-2007, Ext JS, LLC.
2996  *
2997  * Originally Released Under LGPL - original licence link has changed is not relivant.
2998  *
2999  * Fork - LGPL
3000  * <script type="text/javascript">
3001  */
3002
3003
3004 /**
3005  * @class Roo.Shadow
3006  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3007  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3008  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3009  * @constructor
3010  * Create a new Shadow
3011  * @param {Object} config The config object
3012  */
3013 Roo.Shadow = function(config){
3014     Roo.apply(this, config);
3015     if(typeof this.mode != "string"){
3016         this.mode = this.defaultMode;
3017     }
3018     var o = this.offset, a = {h: 0};
3019     var rad = Math.floor(this.offset/2);
3020     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3021         case "drop":
3022             a.w = 0;
3023             a.l = a.t = o;
3024             a.t -= 1;
3025             if(Roo.isIE){
3026                 a.l -= this.offset + rad;
3027                 a.t -= this.offset + rad;
3028                 a.w -= rad;
3029                 a.h -= rad;
3030                 a.t += 1;
3031             }
3032         break;
3033         case "sides":
3034             a.w = (o*2);
3035             a.l = -o;
3036             a.t = o-1;
3037             if(Roo.isIE){
3038                 a.l -= (this.offset - rad);
3039                 a.t -= this.offset + rad;
3040                 a.l += 1;
3041                 a.w -= (this.offset - rad)*2;
3042                 a.w -= rad + 1;
3043                 a.h -= 1;
3044             }
3045         break;
3046         case "frame":
3047             a.w = a.h = (o*2);
3048             a.l = a.t = -o;
3049             a.t += 1;
3050             a.h -= 2;
3051             if(Roo.isIE){
3052                 a.l -= (this.offset - rad);
3053                 a.t -= (this.offset - rad);
3054                 a.l += 1;
3055                 a.w -= (this.offset + rad + 1);
3056                 a.h -= (this.offset + rad);
3057                 a.h += 1;
3058             }
3059         break;
3060     };
3061
3062     this.adjusts = a;
3063 };
3064
3065 Roo.Shadow.prototype = {
3066     /**
3067      * @cfg {String} mode
3068      * The shadow display mode.  Supports the following options:<br />
3069      * sides: Shadow displays on both sides and bottom only<br />
3070      * frame: Shadow displays equally on all four sides<br />
3071      * drop: Traditional bottom-right drop shadow (default)
3072      */
3073     mode: false,
3074     /**
3075      * @cfg {String} offset
3076      * The number of pixels to offset the shadow from the element (defaults to 4)
3077      */
3078     offset: 4,
3079
3080     // private
3081     defaultMode: "drop",
3082
3083     /**
3084      * Displays the shadow under the target element
3085      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3086      */
3087     show : function(target){
3088         target = Roo.get(target);
3089         if(!this.el){
3090             this.el = Roo.Shadow.Pool.pull();
3091             if(this.el.dom.nextSibling != target.dom){
3092                 this.el.insertBefore(target);
3093             }
3094         }
3095         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3096         if(Roo.isIE){
3097             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3098         }
3099         this.realign(
3100             target.getLeft(true),
3101             target.getTop(true),
3102             target.getWidth(),
3103             target.getHeight()
3104         );
3105         this.el.dom.style.display = "block";
3106     },
3107
3108     /**
3109      * Returns true if the shadow is visible, else false
3110      */
3111     isVisible : function(){
3112         return this.el ? true : false;  
3113     },
3114
3115     /**
3116      * Direct alignment when values are already available. Show must be called at least once before
3117      * calling this method to ensure it is initialized.
3118      * @param {Number} left The target element left position
3119      * @param {Number} top The target element top position
3120      * @param {Number} width The target element width
3121      * @param {Number} height The target element height
3122      */
3123     realign : function(l, t, w, h){
3124         if(!this.el){
3125             return;
3126         }
3127         var a = this.adjusts, d = this.el.dom, s = d.style;
3128         var iea = 0;
3129         s.left = (l+a.l)+"px";
3130         s.top = (t+a.t)+"px";
3131         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3132  
3133         if(s.width != sws || s.height != shs){
3134             s.width = sws;
3135             s.height = shs;
3136             if(!Roo.isIE){
3137                 var cn = d.childNodes;
3138                 var sww = Math.max(0, (sw-12))+"px";
3139                 cn[0].childNodes[1].style.width = sww;
3140                 cn[1].childNodes[1].style.width = sww;
3141                 cn[2].childNodes[1].style.width = sww;
3142                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3143             }
3144         }
3145     },
3146
3147     /**
3148      * Hides this shadow
3149      */
3150     hide : function(){
3151         if(this.el){
3152             this.el.dom.style.display = "none";
3153             Roo.Shadow.Pool.push(this.el);
3154             delete this.el;
3155         }
3156     },
3157
3158     /**
3159      * Adjust the z-index of this shadow
3160      * @param {Number} zindex The new z-index
3161      */
3162     setZIndex : function(z){
3163         this.zIndex = z;
3164         if(this.el){
3165             this.el.setStyle("z-index", z);
3166         }
3167     }
3168 };
3169
3170 // Private utility class that manages the internal Shadow cache
3171 Roo.Shadow.Pool = function(){
3172     var p = [];
3173     var markup = Roo.isIE ?
3174                  '<div class="x-ie-shadow"></div>' :
3175                  '<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>';
3176     return {
3177         pull : function(){
3178             var sh = p.shift();
3179             if(!sh){
3180                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3181                 sh.autoBoxAdjust = false;
3182             }
3183             return sh;
3184         },
3185
3186         push : function(sh){
3187             p.push(sh);
3188         }
3189     };
3190 }();/*
3191  * Based on:
3192  * Ext JS Library 1.1.1
3193  * Copyright(c) 2006-2007, Ext JS, LLC.
3194  *
3195  * Originally Released Under LGPL - original licence link has changed is not relivant.
3196  *
3197  * Fork - LGPL
3198  * <script type="text/javascript">
3199  */
3200
3201
3202 /**
3203  * @class Roo.SplitBar
3204  * @extends Roo.util.Observable
3205  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3206  * <br><br>
3207  * Usage:
3208  * <pre><code>
3209 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3210                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3211 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3212 split.minSize = 100;
3213 split.maxSize = 600;
3214 split.animate = true;
3215 split.on('moved', splitterMoved);
3216 </code></pre>
3217  * @constructor
3218  * Create a new SplitBar
3219  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3220  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3221  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3222  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3223                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3224                         position of the SplitBar).
3225  */
3226 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3227     
3228     /** @private */
3229     this.el = Roo.get(dragElement, true);
3230     this.el.dom.unselectable = "on";
3231     /** @private */
3232     this.resizingEl = Roo.get(resizingElement, true);
3233
3234     /**
3235      * @private
3236      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3237      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3238      * @type Number
3239      */
3240     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3241     
3242     /**
3243      * The minimum size of the resizing element. (Defaults to 0)
3244      * @type Number
3245      */
3246     this.minSize = 0;
3247     
3248     /**
3249      * The maximum size of the resizing element. (Defaults to 2000)
3250      * @type Number
3251      */
3252     this.maxSize = 2000;
3253     
3254     /**
3255      * Whether to animate the transition to the new size
3256      * @type Boolean
3257      */
3258     this.animate = false;
3259     
3260     /**
3261      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3262      * @type Boolean
3263      */
3264     this.useShim = false;
3265     
3266     /** @private */
3267     this.shim = null;
3268     
3269     if(!existingProxy){
3270         /** @private */
3271         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3272     }else{
3273         this.proxy = Roo.get(existingProxy).dom;
3274     }
3275     /** @private */
3276     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3277     
3278     /** @private */
3279     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3280     
3281     /** @private */
3282     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3283     
3284     /** @private */
3285     this.dragSpecs = {};
3286     
3287     /**
3288      * @private The adapter to use to positon and resize elements
3289      */
3290     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3291     this.adapter.init(this);
3292     
3293     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3294         /** @private */
3295         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3296         this.el.addClass("x-splitbar-h");
3297     }else{
3298         /** @private */
3299         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3300         this.el.addClass("x-splitbar-v");
3301     }
3302     
3303     this.addEvents({
3304         /**
3305          * @event resize
3306          * Fires when the splitter is moved (alias for {@link #event-moved})
3307          * @param {Roo.SplitBar} this
3308          * @param {Number} newSize the new width or height
3309          */
3310         "resize" : true,
3311         /**
3312          * @event moved
3313          * Fires when the splitter is moved
3314          * @param {Roo.SplitBar} this
3315          * @param {Number} newSize the new width or height
3316          */
3317         "moved" : true,
3318         /**
3319          * @event beforeresize
3320          * Fires before the splitter is dragged
3321          * @param {Roo.SplitBar} this
3322          */
3323         "beforeresize" : true,
3324
3325         "beforeapply" : true
3326     });
3327
3328     Roo.util.Observable.call(this);
3329 };
3330
3331 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3332     onStartProxyDrag : function(x, y){
3333         this.fireEvent("beforeresize", this);
3334         if(!this.overlay){
3335             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3336             o.unselectable();
3337             o.enableDisplayMode("block");
3338             // all splitbars share the same overlay
3339             Roo.SplitBar.prototype.overlay = o;
3340         }
3341         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3342         this.overlay.show();
3343         Roo.get(this.proxy).setDisplayed("block");
3344         var size = this.adapter.getElementSize(this);
3345         this.activeMinSize = this.getMinimumSize();;
3346         this.activeMaxSize = this.getMaximumSize();;
3347         var c1 = size - this.activeMinSize;
3348         var c2 = Math.max(this.activeMaxSize - size, 0);
3349         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3350             this.dd.resetConstraints();
3351             this.dd.setXConstraint(
3352                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3353                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3354             );
3355             this.dd.setYConstraint(0, 0);
3356         }else{
3357             this.dd.resetConstraints();
3358             this.dd.setXConstraint(0, 0);
3359             this.dd.setYConstraint(
3360                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3361                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3362             );
3363          }
3364         this.dragSpecs.startSize = size;
3365         this.dragSpecs.startPoint = [x, y];
3366         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3367     },
3368     
3369     /** 
3370      * @private Called after the drag operation by the DDProxy
3371      */
3372     onEndProxyDrag : function(e){
3373         Roo.get(this.proxy).setDisplayed(false);
3374         var endPoint = Roo.lib.Event.getXY(e);
3375         if(this.overlay){
3376             this.overlay.hide();
3377         }
3378         var newSize;
3379         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3380             newSize = this.dragSpecs.startSize + 
3381                 (this.placement == Roo.SplitBar.LEFT ?
3382                     endPoint[0] - this.dragSpecs.startPoint[0] :
3383                     this.dragSpecs.startPoint[0] - endPoint[0]
3384                 );
3385         }else{
3386             newSize = this.dragSpecs.startSize + 
3387                 (this.placement == Roo.SplitBar.TOP ?
3388                     endPoint[1] - this.dragSpecs.startPoint[1] :
3389                     this.dragSpecs.startPoint[1] - endPoint[1]
3390                 );
3391         }
3392         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3393         if(newSize != this.dragSpecs.startSize){
3394             if(this.fireEvent('beforeapply', this, newSize) !== false){
3395                 this.adapter.setElementSize(this, newSize);
3396                 this.fireEvent("moved", this, newSize);
3397                 this.fireEvent("resize", this, newSize);
3398             }
3399         }
3400     },
3401     
3402     /**
3403      * Get the adapter this SplitBar uses
3404      * @return The adapter object
3405      */
3406     getAdapter : function(){
3407         return this.adapter;
3408     },
3409     
3410     /**
3411      * Set the adapter this SplitBar uses
3412      * @param {Object} adapter A SplitBar adapter object
3413      */
3414     setAdapter : function(adapter){
3415         this.adapter = adapter;
3416         this.adapter.init(this);
3417     },
3418     
3419     /**
3420      * Gets the minimum size for the resizing element
3421      * @return {Number} The minimum size
3422      */
3423     getMinimumSize : function(){
3424         return this.minSize;
3425     },
3426     
3427     /**
3428      * Sets the minimum size for the resizing element
3429      * @param {Number} minSize The minimum size
3430      */
3431     setMinimumSize : function(minSize){
3432         this.minSize = minSize;
3433     },
3434     
3435     /**
3436      * Gets the maximum size for the resizing element
3437      * @return {Number} The maximum size
3438      */
3439     getMaximumSize : function(){
3440         return this.maxSize;
3441     },
3442     
3443     /**
3444      * Sets the maximum size for the resizing element
3445      * @param {Number} maxSize The maximum size
3446      */
3447     setMaximumSize : function(maxSize){
3448         this.maxSize = maxSize;
3449     },
3450     
3451     /**
3452      * Sets the initialize size for the resizing element
3453      * @param {Number} size The initial size
3454      */
3455     setCurrentSize : function(size){
3456         var oldAnimate = this.animate;
3457         this.animate = false;
3458         this.adapter.setElementSize(this, size);
3459         this.animate = oldAnimate;
3460     },
3461     
3462     /**
3463      * Destroy this splitbar. 
3464      * @param {Boolean} removeEl True to remove the element
3465      */
3466     destroy : function(removeEl){
3467         if(this.shim){
3468             this.shim.remove();
3469         }
3470         this.dd.unreg();
3471         this.proxy.parentNode.removeChild(this.proxy);
3472         if(removeEl){
3473             this.el.remove();
3474         }
3475     }
3476 });
3477
3478 /**
3479  * @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.
3480  */
3481 Roo.SplitBar.createProxy = function(dir){
3482     var proxy = new Roo.Element(document.createElement("div"));
3483     proxy.unselectable();
3484     var cls = 'x-splitbar-proxy';
3485     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3486     document.body.appendChild(proxy.dom);
3487     return proxy.dom;
3488 };
3489
3490 /** 
3491  * @class Roo.SplitBar.BasicLayoutAdapter
3492  * Default Adapter. It assumes the splitter and resizing element are not positioned
3493  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3494  */
3495 Roo.SplitBar.BasicLayoutAdapter = function(){
3496 };
3497
3498 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3499     // do nothing for now
3500     init : function(s){
3501     
3502     },
3503     /**
3504      * Called before drag operations to get the current size of the resizing element. 
3505      * @param {Roo.SplitBar} s The SplitBar using this adapter
3506      */
3507      getElementSize : function(s){
3508         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3509             return s.resizingEl.getWidth();
3510         }else{
3511             return s.resizingEl.getHeight();
3512         }
3513     },
3514     
3515     /**
3516      * Called after drag operations to set the size of the resizing element.
3517      * @param {Roo.SplitBar} s The SplitBar using this adapter
3518      * @param {Number} newSize The new size to set
3519      * @param {Function} onComplete A function to be invoked when resizing is complete
3520      */
3521     setElementSize : function(s, newSize, onComplete){
3522         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3523             if(!s.animate){
3524                 s.resizingEl.setWidth(newSize);
3525                 if(onComplete){
3526                     onComplete(s, newSize);
3527                 }
3528             }else{
3529                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3530             }
3531         }else{
3532             
3533             if(!s.animate){
3534                 s.resizingEl.setHeight(newSize);
3535                 if(onComplete){
3536                     onComplete(s, newSize);
3537                 }
3538             }else{
3539                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3540             }
3541         }
3542     }
3543 };
3544
3545 /** 
3546  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3547  * @extends Roo.SplitBar.BasicLayoutAdapter
3548  * Adapter that  moves the splitter element to align with the resized sizing element. 
3549  * Used with an absolute positioned SplitBar.
3550  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3551  * document.body, make sure you assign an id to the body element.
3552  */
3553 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3554     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3555     this.container = Roo.get(container);
3556 };
3557
3558 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3559     init : function(s){
3560         this.basic.init(s);
3561     },
3562     
3563     getElementSize : function(s){
3564         return this.basic.getElementSize(s);
3565     },
3566     
3567     setElementSize : function(s, newSize, onComplete){
3568         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3569     },
3570     
3571     moveSplitter : function(s){
3572         var yes = Roo.SplitBar;
3573         switch(s.placement){
3574             case yes.LEFT:
3575                 s.el.setX(s.resizingEl.getRight());
3576                 break;
3577             case yes.RIGHT:
3578                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3579                 break;
3580             case yes.TOP:
3581                 s.el.setY(s.resizingEl.getBottom());
3582                 break;
3583             case yes.BOTTOM:
3584                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3585                 break;
3586         }
3587     }
3588 };
3589
3590 /**
3591  * Orientation constant - Create a vertical SplitBar
3592  * @static
3593  * @type Number
3594  */
3595 Roo.SplitBar.VERTICAL = 1;
3596
3597 /**
3598  * Orientation constant - Create a horizontal SplitBar
3599  * @static
3600  * @type Number
3601  */
3602 Roo.SplitBar.HORIZONTAL = 2;
3603
3604 /**
3605  * Placement constant - The resizing element is to the left of the splitter element
3606  * @static
3607  * @type Number
3608  */
3609 Roo.SplitBar.LEFT = 1;
3610
3611 /**
3612  * Placement constant - The resizing element is to the right of the splitter element
3613  * @static
3614  * @type Number
3615  */
3616 Roo.SplitBar.RIGHT = 2;
3617
3618 /**
3619  * Placement constant - The resizing element is positioned above the splitter element
3620  * @static
3621  * @type Number
3622  */
3623 Roo.SplitBar.TOP = 3;
3624
3625 /**
3626  * Placement constant - The resizing element is positioned under splitter element
3627  * @static
3628  * @type Number
3629  */
3630 Roo.SplitBar.BOTTOM = 4;
3631 /*
3632  * Based on:
3633  * Ext JS Library 1.1.1
3634  * Copyright(c) 2006-2007, Ext JS, LLC.
3635  *
3636  * Originally Released Under LGPL - original licence link has changed is not relivant.
3637  *
3638  * Fork - LGPL
3639  * <script type="text/javascript">
3640  */
3641
3642 /**
3643  * @class Roo.View
3644  * @extends Roo.util.Observable
3645  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
3646  * This class also supports single and multi selection modes. <br>
3647  * Create a data model bound view:
3648  <pre><code>
3649  var store = new Roo.data.Store(...);
3650
3651  var view = new Roo.View({
3652     el : "my-element",
3653     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
3654  
3655     singleSelect: true,
3656     selectedClass: "ydataview-selected",
3657     store: store
3658  });
3659
3660  // listen for node click?
3661  view.on("click", function(vw, index, node, e){
3662  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3663  });
3664
3665  // load XML data
3666  dataModel.load("foobar.xml");
3667  </code></pre>
3668  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3669  * <br><br>
3670  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3671  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3672  * 
3673  * Note: old style constructor is still suported (container, template, config)
3674  * 
3675  * @constructor
3676  * Create a new View
3677  * @param {Object} config The config object
3678  * 
3679  */
3680 Roo.View = function(config, depreciated_tpl, depreciated_config){
3681     
3682     this.parent = false;
3683     
3684     if (typeof(depreciated_tpl) == 'undefined') {
3685         // new way.. - universal constructor.
3686         Roo.apply(this, config);
3687         this.el  = Roo.get(this.el);
3688     } else {
3689         // old format..
3690         this.el  = Roo.get(config);
3691         this.tpl = depreciated_tpl;
3692         Roo.apply(this, depreciated_config);
3693     }
3694     this.wrapEl  = this.el.wrap().wrap();
3695     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3696     
3697     
3698     if(typeof(this.tpl) == "string"){
3699         this.tpl = new Roo.Template(this.tpl);
3700     } else {
3701         // support xtype ctors..
3702         this.tpl = new Roo.factory(this.tpl, Roo);
3703     }
3704     
3705     
3706     this.tpl.compile();
3707     
3708     /** @private */
3709     this.addEvents({
3710         /**
3711          * @event beforeclick
3712          * Fires before a click is processed. Returns false to cancel the default action.
3713          * @param {Roo.View} this
3714          * @param {Number} index The index of the target node
3715          * @param {HTMLElement} node The target node
3716          * @param {Roo.EventObject} e The raw event object
3717          */
3718             "beforeclick" : true,
3719         /**
3720          * @event click
3721          * Fires when a template node is clicked.
3722          * @param {Roo.View} this
3723          * @param {Number} index The index of the target node
3724          * @param {HTMLElement} node The target node
3725          * @param {Roo.EventObject} e The raw event object
3726          */
3727             "click" : true,
3728         /**
3729          * @event dblclick
3730          * Fires when a template node is double clicked.
3731          * @param {Roo.View} this
3732          * @param {Number} index The index of the target node
3733          * @param {HTMLElement} node The target node
3734          * @param {Roo.EventObject} e The raw event object
3735          */
3736             "dblclick" : true,
3737         /**
3738          * @event contextmenu
3739          * Fires when a template node is right clicked.
3740          * @param {Roo.View} this
3741          * @param {Number} index The index of the target node
3742          * @param {HTMLElement} node The target node
3743          * @param {Roo.EventObject} e The raw event object
3744          */
3745             "contextmenu" : true,
3746         /**
3747          * @event selectionchange
3748          * Fires when the selected nodes change.
3749          * @param {Roo.View} this
3750          * @param {Array} selections Array of the selected nodes
3751          */
3752             "selectionchange" : true,
3753     
3754         /**
3755          * @event beforeselect
3756          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3757          * @param {Roo.View} this
3758          * @param {HTMLElement} node The node to be selected
3759          * @param {Array} selections Array of currently selected nodes
3760          */
3761             "beforeselect" : true,
3762         /**
3763          * @event preparedata
3764          * Fires on every row to render, to allow you to change the data.
3765          * @param {Roo.View} this
3766          * @param {Object} data to be rendered (change this)
3767          */
3768           "preparedata" : true
3769           
3770           
3771         });
3772
3773
3774
3775     this.el.on({
3776         "click": this.onClick,
3777         "dblclick": this.onDblClick,
3778         "contextmenu": this.onContextMenu,
3779         scope:this
3780     });
3781
3782     this.selections = [];
3783     this.nodes = [];
3784     this.cmp = new Roo.CompositeElementLite([]);
3785     if(this.store){
3786         this.store = Roo.factory(this.store, Roo.data);
3787         this.setStore(this.store, true);
3788     }
3789     
3790     if ( this.footer && this.footer.xtype) {
3791            
3792          var fctr = this.wrapEl.appendChild(document.createElement("div"));
3793         
3794         this.footer.dataSource = this.store;
3795         this.footer.container = fctr;
3796         this.footer = Roo.factory(this.footer, Roo);
3797         fctr.insertFirst(this.el);
3798         
3799         // this is a bit insane - as the paging toolbar seems to detach the el..
3800 //        dom.parentNode.parentNode.parentNode
3801          // they get detached?
3802     }
3803     
3804     
3805     Roo.View.superclass.constructor.call(this);
3806     
3807     
3808 };
3809
3810 Roo.extend(Roo.View, Roo.util.Observable, {
3811     
3812      /**
3813      * @cfg {Roo.data.Store} store Data store to load data from.
3814      */
3815     store : false,
3816     
3817     /**
3818      * @cfg {String|Roo.Element} el The container element.
3819      */
3820     el : '',
3821     
3822     /**
3823      * @cfg {String|Roo.Template} tpl The template used by this View 
3824      */
3825     tpl : false,
3826     /**
3827      * @cfg {String} dataName the named area of the template to use as the data area
3828      *                          Works with domtemplates roo-name="name"
3829      */
3830     dataName: false,
3831     /**
3832      * @cfg {String} selectedClass The css class to add to selected nodes
3833      */
3834     selectedClass : "x-view-selected",
3835      /**
3836      * @cfg {String} emptyText The empty text to show when nothing is loaded.
3837      */
3838     emptyText : "",
3839     
3840     /**
3841      * @cfg {String} text to display on mask (default Loading)
3842      */
3843     mask : false,
3844     /**
3845      * @cfg {Boolean} multiSelect Allow multiple selection
3846      */
3847     multiSelect : false,
3848     /**
3849      * @cfg {Boolean} singleSelect Allow single selection
3850      */
3851     singleSelect:  false,
3852     
3853     /**
3854      * @cfg {Boolean} toggleSelect - selecting 
3855      */
3856     toggleSelect : false,
3857     
3858     /**
3859      * @cfg {Boolean} tickable - selecting 
3860      */
3861     tickable : false,
3862     
3863     /**
3864      * Returns the element this view is bound to.
3865      * @return {Roo.Element}
3866      */
3867     getEl : function(){
3868         return this.wrapEl;
3869     },
3870     
3871     
3872
3873     /**
3874      * Refreshes the view. - called by datachanged on the store. - do not call directly.
3875      */
3876     refresh : function(){
3877         //Roo.log('refresh');
3878         var t = this.tpl;
3879         
3880         // if we are using something like 'domtemplate', then
3881         // the what gets used is:
3882         // t.applySubtemplate(NAME, data, wrapping data..)
3883         // the outer template then get' applied with
3884         //     the store 'extra data'
3885         // and the body get's added to the
3886         //      roo-name="data" node?
3887         //      <span class='roo-tpl-{name}'></span> ?????
3888         
3889         
3890         
3891         this.clearSelections();
3892         this.el.update("");
3893         var html = [];
3894         var records = this.store.getRange();
3895         if(records.length < 1) {
3896             
3897             // is this valid??  = should it render a template??
3898             
3899             this.el.update(this.emptyText);
3900             return;
3901         }
3902         var el = this.el;
3903         if (this.dataName) {
3904             this.el.update(t.apply(this.store.meta)); //????
3905             el = this.el.child('.roo-tpl-' + this.dataName);
3906         }
3907         
3908         for(var i = 0, len = records.length; i < len; i++){
3909             var data = this.prepareData(records[i].data, i, records[i]);
3910             this.fireEvent("preparedata", this, data, i, records[i]);
3911             
3912             var d = Roo.apply({}, data);
3913             
3914             if(this.tickable){
3915                 Roo.apply(d, {'roo-id' : Roo.id()});
3916                 
3917                 var _this = this;
3918             
3919                 Roo.each(this.parent.item, function(item){
3920                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3921                         return;
3922                     }
3923                     Roo.apply(d, {'roo-data-checked' : 'checked'});
3924                 });
3925             }
3926             
3927             html[html.length] = Roo.util.Format.trim(
3928                 this.dataName ?
3929                     t.applySubtemplate(this.dataName, d, this.store.meta) :
3930                     t.apply(d)
3931             );
3932         }
3933         
3934         
3935         
3936         el.update(html.join(""));
3937         this.nodes = el.dom.childNodes;
3938         this.updateIndexes(0);
3939     },
3940     
3941
3942     /**
3943      * Function to override to reformat the data that is sent to
3944      * the template for each node.
3945      * DEPRICATED - use the preparedata event handler.
3946      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3947      * a JSON object for an UpdateManager bound view).
3948      */
3949     prepareData : function(data, index, record)
3950     {
3951         this.fireEvent("preparedata", this, data, index, record);
3952         return data;
3953     },
3954
3955     onUpdate : function(ds, record){
3956         // Roo.log('on update');   
3957         this.clearSelections();
3958         var index = this.store.indexOf(record);
3959         var n = this.nodes[index];
3960         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3961         n.parentNode.removeChild(n);
3962         this.updateIndexes(index, index);
3963     },
3964
3965     
3966     
3967 // --------- FIXME     
3968     onAdd : function(ds, records, index)
3969     {
3970         //Roo.log(['on Add', ds, records, index] );        
3971         this.clearSelections();
3972         if(this.nodes.length == 0){
3973             this.refresh();
3974             return;
3975         }
3976         var n = this.nodes[index];
3977         for(var i = 0, len = records.length; i < len; i++){
3978             var d = this.prepareData(records[i].data, i, records[i]);
3979             if(n){
3980                 this.tpl.insertBefore(n, d);
3981             }else{
3982                 
3983                 this.tpl.append(this.el, d);
3984             }
3985         }
3986         this.updateIndexes(index);
3987     },
3988
3989     onRemove : function(ds, record, index){
3990        // Roo.log('onRemove');
3991         this.clearSelections();
3992         var el = this.dataName  ?
3993             this.el.child('.roo-tpl-' + this.dataName) :
3994             this.el; 
3995         
3996         el.dom.removeChild(this.nodes[index]);
3997         this.updateIndexes(index);
3998     },
3999
4000     /**
4001      * Refresh an individual node.
4002      * @param {Number} index
4003      */
4004     refreshNode : function(index){
4005         this.onUpdate(this.store, this.store.getAt(index));
4006     },
4007
4008     updateIndexes : function(startIndex, endIndex){
4009         var ns = this.nodes;
4010         startIndex = startIndex || 0;
4011         endIndex = endIndex || ns.length - 1;
4012         for(var i = startIndex; i <= endIndex; i++){
4013             ns[i].nodeIndex = i;
4014         }
4015     },
4016
4017     /**
4018      * Changes the data store this view uses and refresh the view.
4019      * @param {Store} store
4020      */
4021     setStore : function(store, initial){
4022         if(!initial && this.store){
4023             this.store.un("datachanged", this.refresh);
4024             this.store.un("add", this.onAdd);
4025             this.store.un("remove", this.onRemove);
4026             this.store.un("update", this.onUpdate);
4027             this.store.un("clear", this.refresh);
4028             this.store.un("beforeload", this.onBeforeLoad);
4029             this.store.un("load", this.onLoad);
4030             this.store.un("loadexception", this.onLoad);
4031         }
4032         if(store){
4033           
4034             store.on("datachanged", this.refresh, this);
4035             store.on("add", this.onAdd, this);
4036             store.on("remove", this.onRemove, this);
4037             store.on("update", this.onUpdate, this);
4038             store.on("clear", this.refresh, this);
4039             store.on("beforeload", this.onBeforeLoad, this);
4040             store.on("load", this.onLoad, this);
4041             store.on("loadexception", this.onLoad, this);
4042         }
4043         
4044         if(store){
4045             this.refresh();
4046         }
4047     },
4048     /**
4049      * onbeforeLoad - masks the loading area.
4050      *
4051      */
4052     onBeforeLoad : function(store,opts)
4053     {
4054          //Roo.log('onBeforeLoad');   
4055         if (!opts.add) {
4056             this.el.update("");
4057         }
4058         this.el.mask(this.mask ? this.mask : "Loading" ); 
4059     },
4060     onLoad : function ()
4061     {
4062         this.el.unmask();
4063     },
4064     
4065
4066     /**
4067      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4068      * @param {HTMLElement} node
4069      * @return {HTMLElement} The template node
4070      */
4071     findItemFromChild : function(node){
4072         var el = this.dataName  ?
4073             this.el.child('.roo-tpl-' + this.dataName,true) :
4074             this.el.dom; 
4075         
4076         if(!node || node.parentNode == el){
4077                     return node;
4078             }
4079             var p = node.parentNode;
4080             while(p && p != el){
4081             if(p.parentNode == el){
4082                 return p;
4083             }
4084             p = p.parentNode;
4085         }
4086             return null;
4087     },
4088
4089     /** @ignore */
4090     onClick : function(e){
4091         var item = this.findItemFromChild(e.getTarget());
4092         if(item){
4093             var index = this.indexOf(item);
4094             if(this.onItemClick(item, index, e) !== false){
4095                 this.fireEvent("click", this, index, item, e);
4096             }
4097         }else{
4098             this.clearSelections();
4099         }
4100     },
4101
4102     /** @ignore */
4103     onContextMenu : function(e){
4104         var item = this.findItemFromChild(e.getTarget());
4105         if(item){
4106             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4107         }
4108     },
4109
4110     /** @ignore */
4111     onDblClick : function(e){
4112         var item = this.findItemFromChild(e.getTarget());
4113         if(item){
4114             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4115         }
4116     },
4117
4118     onItemClick : function(item, index, e)
4119     {
4120         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4121             return false;
4122         }
4123         if (this.toggleSelect) {
4124             var m = this.isSelected(item) ? 'unselect' : 'select';
4125             //Roo.log(m);
4126             var _t = this;
4127             _t[m](item, true, false);
4128             return true;
4129         }
4130         if(this.multiSelect || this.singleSelect){
4131             if(this.multiSelect && e.shiftKey && this.lastSelection){
4132                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4133             }else{
4134                 this.select(item, this.multiSelect && e.ctrlKey);
4135                 this.lastSelection = item;
4136             }
4137             
4138             if(!this.tickable){
4139                 e.preventDefault();
4140             }
4141             
4142         }
4143         return true;
4144     },
4145
4146     /**
4147      * Get the number of selected nodes.
4148      * @return {Number}
4149      */
4150     getSelectionCount : function(){
4151         return this.selections.length;
4152     },
4153
4154     /**
4155      * Get the currently selected nodes.
4156      * @return {Array} An array of HTMLElements
4157      */
4158     getSelectedNodes : function(){
4159         return this.selections;
4160     },
4161
4162     /**
4163      * Get the indexes of the selected nodes.
4164      * @return {Array}
4165      */
4166     getSelectedIndexes : function(){
4167         var indexes = [], s = this.selections;
4168         for(var i = 0, len = s.length; i < len; i++){
4169             indexes.push(s[i].nodeIndex);
4170         }
4171         return indexes;
4172     },
4173
4174     /**
4175      * Clear all selections
4176      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4177      */
4178     clearSelections : function(suppressEvent){
4179         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4180             this.cmp.elements = this.selections;
4181             this.cmp.removeClass(this.selectedClass);
4182             this.selections = [];
4183             if(!suppressEvent){
4184                 this.fireEvent("selectionchange", this, this.selections);
4185             }
4186         }
4187     },
4188
4189     /**
4190      * Returns true if the passed node is selected
4191      * @param {HTMLElement/Number} node The node or node index
4192      * @return {Boolean}
4193      */
4194     isSelected : function(node){
4195         var s = this.selections;
4196         if(s.length < 1){
4197             return false;
4198         }
4199         node = this.getNode(node);
4200         return s.indexOf(node) !== -1;
4201     },
4202
4203     /**
4204      * Selects nodes.
4205      * @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
4206      * @param {Boolean} keepExisting (optional) true to keep existing selections
4207      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4208      */
4209     select : function(nodeInfo, keepExisting, suppressEvent){
4210         if(nodeInfo instanceof Array){
4211             if(!keepExisting){
4212                 this.clearSelections(true);
4213             }
4214             for(var i = 0, len = nodeInfo.length; i < len; i++){
4215                 this.select(nodeInfo[i], true, true);
4216             }
4217             return;
4218         } 
4219         var node = this.getNode(nodeInfo);
4220         if(!node || this.isSelected(node)){
4221             return; // already selected.
4222         }
4223         if(!keepExisting){
4224             this.clearSelections(true);
4225         }
4226         
4227         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4228             Roo.fly(node).addClass(this.selectedClass);
4229             this.selections.push(node);
4230             if(!suppressEvent){
4231                 this.fireEvent("selectionchange", this, this.selections);
4232             }
4233         }
4234         
4235         
4236     },
4237       /**
4238      * Unselects nodes.
4239      * @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
4240      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4241      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4242      */
4243     unselect : function(nodeInfo, keepExisting, suppressEvent)
4244     {
4245         if(nodeInfo instanceof Array){
4246             Roo.each(this.selections, function(s) {
4247                 this.unselect(s, nodeInfo);
4248             }, this);
4249             return;
4250         }
4251         var node = this.getNode(nodeInfo);
4252         if(!node || !this.isSelected(node)){
4253             //Roo.log("not selected");
4254             return; // not selected.
4255         }
4256         // fireevent???
4257         var ns = [];
4258         Roo.each(this.selections, function(s) {
4259             if (s == node ) {
4260                 Roo.fly(node).removeClass(this.selectedClass);
4261
4262                 return;
4263             }
4264             ns.push(s);
4265         },this);
4266         
4267         this.selections= ns;
4268         this.fireEvent("selectionchange", this, this.selections);
4269     },
4270
4271     /**
4272      * Gets a template node.
4273      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4274      * @return {HTMLElement} The node or null if it wasn't found
4275      */
4276     getNode : function(nodeInfo){
4277         if(typeof nodeInfo == "string"){
4278             return document.getElementById(nodeInfo);
4279         }else if(typeof nodeInfo == "number"){
4280             return this.nodes[nodeInfo];
4281         }
4282         return nodeInfo;
4283     },
4284
4285     /**
4286      * Gets a range template nodes.
4287      * @param {Number} startIndex
4288      * @param {Number} endIndex
4289      * @return {Array} An array of nodes
4290      */
4291     getNodes : function(start, end){
4292         var ns = this.nodes;
4293         start = start || 0;
4294         end = typeof end == "undefined" ? ns.length - 1 : end;
4295         var nodes = [];
4296         if(start <= end){
4297             for(var i = start; i <= end; i++){
4298                 nodes.push(ns[i]);
4299             }
4300         } else{
4301             for(var i = start; i >= end; i--){
4302                 nodes.push(ns[i]);
4303             }
4304         }
4305         return nodes;
4306     },
4307
4308     /**
4309      * Finds the index of the passed node
4310      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4311      * @return {Number} The index of the node or -1
4312      */
4313     indexOf : function(node){
4314         node = this.getNode(node);
4315         if(typeof node.nodeIndex == "number"){
4316             return node.nodeIndex;
4317         }
4318         var ns = this.nodes;
4319         for(var i = 0, len = ns.length; i < len; i++){
4320             if(ns[i] == node){
4321                 return i;
4322             }
4323         }
4324         return -1;
4325     }
4326 });
4327 /*
4328  * Based on:
4329  * Ext JS Library 1.1.1
4330  * Copyright(c) 2006-2007, Ext JS, LLC.
4331  *
4332  * Originally Released Under LGPL - original licence link has changed is not relivant.
4333  *
4334  * Fork - LGPL
4335  * <script type="text/javascript">
4336  */
4337
4338 /**
4339  * @class Roo.JsonView
4340  * @extends Roo.View
4341  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4342 <pre><code>
4343 var view = new Roo.JsonView({
4344     container: "my-element",
4345     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4346     multiSelect: true, 
4347     jsonRoot: "data" 
4348 });
4349
4350 // listen for node click?
4351 view.on("click", function(vw, index, node, e){
4352     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4353 });
4354
4355 // direct load of JSON data
4356 view.load("foobar.php");
4357
4358 // Example from my blog list
4359 var tpl = new Roo.Template(
4360     '&lt;div class="entry"&gt;' +
4361     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4362     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4363     "&lt;/div&gt;&lt;hr /&gt;"
4364 );
4365
4366 var moreView = new Roo.JsonView({
4367     container :  "entry-list", 
4368     template : tpl,
4369     jsonRoot: "posts"
4370 });
4371 moreView.on("beforerender", this.sortEntries, this);
4372 moreView.load({
4373     url: "/blog/get-posts.php",
4374     params: "allposts=true",
4375     text: "Loading Blog Entries..."
4376 });
4377 </code></pre>
4378
4379 * Note: old code is supported with arguments : (container, template, config)
4380
4381
4382  * @constructor
4383  * Create a new JsonView
4384  * 
4385  * @param {Object} config The config object
4386  * 
4387  */
4388 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4389     
4390     
4391     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4392
4393     var um = this.el.getUpdateManager();
4394     um.setRenderer(this);
4395     um.on("update", this.onLoad, this);
4396     um.on("failure", this.onLoadException, this);
4397
4398     /**
4399      * @event beforerender
4400      * Fires before rendering of the downloaded JSON data.
4401      * @param {Roo.JsonView} this
4402      * @param {Object} data The JSON data loaded
4403      */
4404     /**
4405      * @event load
4406      * Fires when data is loaded.
4407      * @param {Roo.JsonView} this
4408      * @param {Object} data The JSON data loaded
4409      * @param {Object} response The raw Connect response object
4410      */
4411     /**
4412      * @event loadexception
4413      * Fires when loading fails.
4414      * @param {Roo.JsonView} this
4415      * @param {Object} response The raw Connect response object
4416      */
4417     this.addEvents({
4418         'beforerender' : true,
4419         'load' : true,
4420         'loadexception' : true
4421     });
4422 };
4423 Roo.extend(Roo.JsonView, Roo.View, {
4424     /**
4425      * @type {String} The root property in the loaded JSON object that contains the data
4426      */
4427     jsonRoot : "",
4428
4429     /**
4430      * Refreshes the view.
4431      */
4432     refresh : function(){
4433         this.clearSelections();
4434         this.el.update("");
4435         var html = [];
4436         var o = this.jsonData;
4437         if(o && o.length > 0){
4438             for(var i = 0, len = o.length; i < len; i++){
4439                 var data = this.prepareData(o[i], i, o);
4440                 html[html.length] = this.tpl.apply(data);
4441             }
4442         }else{
4443             html.push(this.emptyText);
4444         }
4445         this.el.update(html.join(""));
4446         this.nodes = this.el.dom.childNodes;
4447         this.updateIndexes(0);
4448     },
4449
4450     /**
4451      * 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.
4452      * @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:
4453      <pre><code>
4454      view.load({
4455          url: "your-url.php",
4456          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4457          callback: yourFunction,
4458          scope: yourObject, //(optional scope)
4459          discardUrl: false,
4460          nocache: false,
4461          text: "Loading...",
4462          timeout: 30,
4463          scripts: false
4464      });
4465      </code></pre>
4466      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4467      * 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.
4468      * @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}
4469      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4470      * @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.
4471      */
4472     load : function(){
4473         var um = this.el.getUpdateManager();
4474         um.update.apply(um, arguments);
4475     },
4476
4477     // note - render is a standard framework call...
4478     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4479     render : function(el, response){
4480         
4481         this.clearSelections();
4482         this.el.update("");
4483         var o;
4484         try{
4485             if (response != '') {
4486                 o = Roo.util.JSON.decode(response.responseText);
4487                 if(this.jsonRoot){
4488                     
4489                     o = o[this.jsonRoot];
4490                 }
4491             }
4492         } catch(e){
4493         }
4494         /**
4495          * The current JSON data or null
4496          */
4497         this.jsonData = o;
4498         this.beforeRender();
4499         this.refresh();
4500     },
4501
4502 /**
4503  * Get the number of records in the current JSON dataset
4504  * @return {Number}
4505  */
4506     getCount : function(){
4507         return this.jsonData ? this.jsonData.length : 0;
4508     },
4509
4510 /**
4511  * Returns the JSON object for the specified node(s)
4512  * @param {HTMLElement/Array} node The node or an array of nodes
4513  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4514  * you get the JSON object for the node
4515  */
4516     getNodeData : function(node){
4517         if(node instanceof Array){
4518             var data = [];
4519             for(var i = 0, len = node.length; i < len; i++){
4520                 data.push(this.getNodeData(node[i]));
4521             }
4522             return data;
4523         }
4524         return this.jsonData[this.indexOf(node)] || null;
4525     },
4526
4527     beforeRender : function(){
4528         this.snapshot = this.jsonData;
4529         if(this.sortInfo){
4530             this.sort.apply(this, this.sortInfo);
4531         }
4532         this.fireEvent("beforerender", this, this.jsonData);
4533     },
4534
4535     onLoad : function(el, o){
4536         this.fireEvent("load", this, this.jsonData, o);
4537     },
4538
4539     onLoadException : function(el, o){
4540         this.fireEvent("loadexception", this, o);
4541     },
4542
4543 /**
4544  * Filter the data by a specific property.
4545  * @param {String} property A property on your JSON objects
4546  * @param {String/RegExp} value Either string that the property values
4547  * should start with, or a RegExp to test against the property
4548  */
4549     filter : function(property, value){
4550         if(this.jsonData){
4551             var data = [];
4552             var ss = this.snapshot;
4553             if(typeof value == "string"){
4554                 var vlen = value.length;
4555                 if(vlen == 0){
4556                     this.clearFilter();
4557                     return;
4558                 }
4559                 value = value.toLowerCase();
4560                 for(var i = 0, len = ss.length; i < len; i++){
4561                     var o = ss[i];
4562                     if(o[property].substr(0, vlen).toLowerCase() == value){
4563                         data.push(o);
4564                     }
4565                 }
4566             } else if(value.exec){ // regex?
4567                 for(var i = 0, len = ss.length; i < len; i++){
4568                     var o = ss[i];
4569                     if(value.test(o[property])){
4570                         data.push(o);
4571                     }
4572                 }
4573             } else{
4574                 return;
4575             }
4576             this.jsonData = data;
4577             this.refresh();
4578         }
4579     },
4580
4581 /**
4582  * Filter by a function. The passed function will be called with each
4583  * object in the current dataset. If the function returns true the value is kept,
4584  * otherwise it is filtered.
4585  * @param {Function} fn
4586  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4587  */
4588     filterBy : function(fn, scope){
4589         if(this.jsonData){
4590             var data = [];
4591             var ss = this.snapshot;
4592             for(var i = 0, len = ss.length; i < len; i++){
4593                 var o = ss[i];
4594                 if(fn.call(scope || this, o)){
4595                     data.push(o);
4596                 }
4597             }
4598             this.jsonData = data;
4599             this.refresh();
4600         }
4601     },
4602
4603 /**
4604  * Clears the current filter.
4605  */
4606     clearFilter : function(){
4607         if(this.snapshot && this.jsonData != this.snapshot){
4608             this.jsonData = this.snapshot;
4609             this.refresh();
4610         }
4611     },
4612
4613
4614 /**
4615  * Sorts the data for this view and refreshes it.
4616  * @param {String} property A property on your JSON objects to sort on
4617  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4618  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4619  */
4620     sort : function(property, dir, sortType){
4621         this.sortInfo = Array.prototype.slice.call(arguments, 0);
4622         if(this.jsonData){
4623             var p = property;
4624             var dsc = dir && dir.toLowerCase() == "desc";
4625             var f = function(o1, o2){
4626                 var v1 = sortType ? sortType(o1[p]) : o1[p];
4627                 var v2 = sortType ? sortType(o2[p]) : o2[p];
4628                 ;
4629                 if(v1 < v2){
4630                     return dsc ? +1 : -1;
4631                 } else if(v1 > v2){
4632                     return dsc ? -1 : +1;
4633                 } else{
4634                     return 0;
4635                 }
4636             };
4637             this.jsonData.sort(f);
4638             this.refresh();
4639             if(this.jsonData != this.snapshot){
4640                 this.snapshot.sort(f);
4641             }
4642         }
4643     }
4644 });/*
4645  * Based on:
4646  * Ext JS Library 1.1.1
4647  * Copyright(c) 2006-2007, Ext JS, LLC.
4648  *
4649  * Originally Released Under LGPL - original licence link has changed is not relivant.
4650  *
4651  * Fork - LGPL
4652  * <script type="text/javascript">
4653  */
4654  
4655
4656 /**
4657  * @class Roo.ColorPalette
4658  * @extends Roo.Component
4659  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
4660  * Here's an example of typical usage:
4661  * <pre><code>
4662 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
4663 cp.render('my-div');
4664
4665 cp.on('select', function(palette, selColor){
4666     // do something with selColor
4667 });
4668 </code></pre>
4669  * @constructor
4670  * Create a new ColorPalette
4671  * @param {Object} config The config object
4672  */
4673 Roo.ColorPalette = function(config){
4674     Roo.ColorPalette.superclass.constructor.call(this, config);
4675     this.addEvents({
4676         /**
4677              * @event select
4678              * Fires when a color is selected
4679              * @param {ColorPalette} this
4680              * @param {String} color The 6-digit color hex code (without the # symbol)
4681              */
4682         select: true
4683     });
4684
4685     if(this.handler){
4686         this.on("select", this.handler, this.scope, true);
4687     }
4688 };
4689 Roo.extend(Roo.ColorPalette, Roo.Component, {
4690     /**
4691      * @cfg {String} itemCls
4692      * The CSS class to apply to the containing element (defaults to "x-color-palette")
4693      */
4694     itemCls : "x-color-palette",
4695     /**
4696      * @cfg {String} value
4697      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
4698      * the hex codes are case-sensitive.
4699      */
4700     value : null,
4701     clickEvent:'click',
4702     // private
4703     ctype: "Roo.ColorPalette",
4704
4705     /**
4706      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4707      */
4708     allowReselect : false,
4709
4710     /**
4711      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
4712      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
4713      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4714      * of colors with the width setting until the box is symmetrical.</p>
4715      * <p>You can override individual colors if needed:</p>
4716      * <pre><code>
4717 var cp = new Roo.ColorPalette();
4718 cp.colors[0] = "FF0000";  // change the first box to red
4719 </code></pre>
4720
4721 Or you can provide a custom array of your own for complete control:
4722 <pre><code>
4723 var cp = new Roo.ColorPalette();
4724 cp.colors = ["000000", "993300", "333300"];
4725 </code></pre>
4726      * @type Array
4727      */
4728     colors : [
4729         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4730         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4731         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4732         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4733         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4734     ],
4735
4736     // private
4737     onRender : function(container, position){
4738         var t = new Roo.MasterTemplate(
4739             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
4740         );
4741         var c = this.colors;
4742         for(var i = 0, len = c.length; i < len; i++){
4743             t.add([c[i]]);
4744         }
4745         var el = document.createElement("div");
4746         el.className = this.itemCls;
4747         t.overwrite(el);
4748         container.dom.insertBefore(el, position);
4749         this.el = Roo.get(el);
4750         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
4751         if(this.clickEvent != 'click'){
4752             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
4753         }
4754     },
4755
4756     // private
4757     afterRender : function(){
4758         Roo.ColorPalette.superclass.afterRender.call(this);
4759         if(this.value){
4760             var s = this.value;
4761             this.value = null;
4762             this.select(s);
4763         }
4764     },
4765
4766     // private
4767     handleClick : function(e, t){
4768         e.preventDefault();
4769         if(!this.disabled){
4770             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4771             this.select(c.toUpperCase());
4772         }
4773     },
4774
4775     /**
4776      * Selects the specified color in the palette (fires the select event)
4777      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4778      */
4779     select : function(color){
4780         color = color.replace("#", "");
4781         if(color != this.value || this.allowReselect){
4782             var el = this.el;
4783             if(this.value){
4784                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4785             }
4786             el.child("a.color-"+color).addClass("x-color-palette-sel");
4787             this.value = color;
4788             this.fireEvent("select", this, color);
4789         }
4790     }
4791 });/*
4792  * Based on:
4793  * Ext JS Library 1.1.1
4794  * Copyright(c) 2006-2007, Ext JS, LLC.
4795  *
4796  * Originally Released Under LGPL - original licence link has changed is not relivant.
4797  *
4798  * Fork - LGPL
4799  * <script type="text/javascript">
4800  */
4801  
4802 /**
4803  * @class Roo.DatePicker
4804  * @extends Roo.Component
4805  * Simple date picker class.
4806  * @constructor
4807  * Create a new DatePicker
4808  * @param {Object} config The config object
4809  */
4810 Roo.DatePicker = function(config){
4811     Roo.DatePicker.superclass.constructor.call(this, config);
4812
4813     this.value = config && config.value ?
4814                  config.value.clearTime() : new Date().clearTime();
4815
4816     this.addEvents({
4817         /**
4818              * @event select
4819              * Fires when a date is selected
4820              * @param {DatePicker} this
4821              * @param {Date} date The selected date
4822              */
4823         'select': true,
4824         /**
4825              * @event monthchange
4826              * Fires when the displayed month changes 
4827              * @param {DatePicker} this
4828              * @param {Date} date The selected month
4829              */
4830         'monthchange': true
4831     });
4832
4833     if(this.handler){
4834         this.on("select", this.handler,  this.scope || this);
4835     }
4836     // build the disabledDatesRE
4837     if(!this.disabledDatesRE && this.disabledDates){
4838         var dd = this.disabledDates;
4839         var re = "(?:";
4840         for(var i = 0; i < dd.length; i++){
4841             re += dd[i];
4842             if(i != dd.length-1) {
4843                 re += "|";
4844             }
4845         }
4846         this.disabledDatesRE = new RegExp(re + ")");
4847     }
4848 };
4849
4850 Roo.extend(Roo.DatePicker, Roo.Component, {
4851     /**
4852      * @cfg {String} todayText
4853      * The text to display on the button that selects the current date (defaults to "Today")
4854      */
4855     todayText : "Today",
4856     /**
4857      * @cfg {String} okText
4858      * The text to display on the ok button
4859      */
4860     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
4861     /**
4862      * @cfg {String} cancelText
4863      * The text to display on the cancel button
4864      */
4865     cancelText : "Cancel",
4866     /**
4867      * @cfg {String} todayTip
4868      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4869      */
4870     todayTip : "{0} (Spacebar)",
4871     /**
4872      * @cfg {Date} minDate
4873      * Minimum allowable date (JavaScript date object, defaults to null)
4874      */
4875     minDate : null,
4876     /**
4877      * @cfg {Date} maxDate
4878      * Maximum allowable date (JavaScript date object, defaults to null)
4879      */
4880     maxDate : null,
4881     /**
4882      * @cfg {String} minText
4883      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4884      */
4885     minText : "This date is before the minimum date",
4886     /**
4887      * @cfg {String} maxText
4888      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4889      */
4890     maxText : "This date is after the maximum date",
4891     /**
4892      * @cfg {String} format
4893      * The default date format string which can be overriden for localization support.  The format must be
4894      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4895      */
4896     format : "m/d/y",
4897     /**
4898      * @cfg {Array} disabledDays
4899      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4900      */
4901     disabledDays : null,
4902     /**
4903      * @cfg {String} disabledDaysText
4904      * The tooltip to display when the date falls on a disabled day (defaults to "")
4905      */
4906     disabledDaysText : "",
4907     /**
4908      * @cfg {RegExp} disabledDatesRE
4909      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4910      */
4911     disabledDatesRE : null,
4912     /**
4913      * @cfg {String} disabledDatesText
4914      * The tooltip text to display when the date falls on a disabled date (defaults to "")
4915      */
4916     disabledDatesText : "",
4917     /**
4918      * @cfg {Boolean} constrainToViewport
4919      * True to constrain the date picker to the viewport (defaults to true)
4920      */
4921     constrainToViewport : true,
4922     /**
4923      * @cfg {Array} monthNames
4924      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4925      */
4926     monthNames : Date.monthNames,
4927     /**
4928      * @cfg {Array} dayNames
4929      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4930      */
4931     dayNames : Date.dayNames,
4932     /**
4933      * @cfg {String} nextText
4934      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4935      */
4936     nextText: 'Next Month (Control+Right)',
4937     /**
4938      * @cfg {String} prevText
4939      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4940      */
4941     prevText: 'Previous Month (Control+Left)',
4942     /**
4943      * @cfg {String} monthYearText
4944      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4945      */
4946     monthYearText: 'Choose a month (Control+Up/Down to move years)',
4947     /**
4948      * @cfg {Number} startDay
4949      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4950      */
4951     startDay : 0,
4952     /**
4953      * @cfg {Bool} showClear
4954      * Show a clear button (usefull for date form elements that can be blank.)
4955      */
4956     
4957     showClear: false,
4958     
4959     /**
4960      * Sets the value of the date field
4961      * @param {Date} value The date to set
4962      */
4963     setValue : function(value){
4964         var old = this.value;
4965         
4966         if (typeof(value) == 'string') {
4967          
4968             value = Date.parseDate(value, this.format);
4969         }
4970         if (!value) {
4971             value = new Date();
4972         }
4973         
4974         this.value = value.clearTime(true);
4975         if(this.el){
4976             this.update(this.value);
4977         }
4978     },
4979
4980     /**
4981      * Gets the current selected value of the date field
4982      * @return {Date} The selected date
4983      */
4984     getValue : function(){
4985         return this.value;
4986     },
4987
4988     // private
4989     focus : function(){
4990         if(this.el){
4991             this.update(this.activeDate);
4992         }
4993     },
4994
4995     // privateval
4996     onRender : function(container, position){
4997         
4998         var m = [
4999              '<table cellspacing="0">',
5000                 '<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>',
5001                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5002         var dn = this.dayNames;
5003         for(var i = 0; i < 7; i++){
5004             var d = this.startDay+i;
5005             if(d > 6){
5006                 d = d-7;
5007             }
5008             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5009         }
5010         m[m.length] = "</tr></thead><tbody><tr>";
5011         for(var i = 0; i < 42; i++) {
5012             if(i % 7 == 0 && i != 0){
5013                 m[m.length] = "</tr><tr>";
5014             }
5015             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5016         }
5017         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5018             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5019
5020         var el = document.createElement("div");
5021         el.className = "x-date-picker";
5022         el.innerHTML = m.join("");
5023
5024         container.dom.insertBefore(el, position);
5025
5026         this.el = Roo.get(el);
5027         this.eventEl = Roo.get(el.firstChild);
5028
5029         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5030             handler: this.showPrevMonth,
5031             scope: this,
5032             preventDefault:true,
5033             stopDefault:true
5034         });
5035
5036         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5037             handler: this.showNextMonth,
5038             scope: this,
5039             preventDefault:true,
5040             stopDefault:true
5041         });
5042
5043         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5044
5045         this.monthPicker = this.el.down('div.x-date-mp');
5046         this.monthPicker.enableDisplayMode('block');
5047         
5048         var kn = new Roo.KeyNav(this.eventEl, {
5049             "left" : function(e){
5050                 e.ctrlKey ?
5051                     this.showPrevMonth() :
5052                     this.update(this.activeDate.add("d", -1));
5053             },
5054
5055             "right" : function(e){
5056                 e.ctrlKey ?
5057                     this.showNextMonth() :
5058                     this.update(this.activeDate.add("d", 1));
5059             },
5060
5061             "up" : function(e){
5062                 e.ctrlKey ?
5063                     this.showNextYear() :
5064                     this.update(this.activeDate.add("d", -7));
5065             },
5066
5067             "down" : function(e){
5068                 e.ctrlKey ?
5069                     this.showPrevYear() :
5070                     this.update(this.activeDate.add("d", 7));
5071             },
5072
5073             "pageUp" : function(e){
5074                 this.showNextMonth();
5075             },
5076
5077             "pageDown" : function(e){
5078                 this.showPrevMonth();
5079             },
5080
5081             "enter" : function(e){
5082                 e.stopPropagation();
5083                 return true;
5084             },
5085
5086             scope : this
5087         });
5088
5089         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5090
5091         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5092
5093         this.el.unselectable();
5094         
5095         this.cells = this.el.select("table.x-date-inner tbody td");
5096         this.textNodes = this.el.query("table.x-date-inner tbody span");
5097
5098         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5099             text: "&#160;",
5100             tooltip: this.monthYearText
5101         });
5102
5103         this.mbtn.on('click', this.showMonthPicker, this);
5104         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5105
5106
5107         var today = (new Date()).dateFormat(this.format);
5108         
5109         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5110         if (this.showClear) {
5111             baseTb.add( new Roo.Toolbar.Fill());
5112         }
5113         baseTb.add({
5114             text: String.format(this.todayText, today),
5115             tooltip: String.format(this.todayTip, today),
5116             handler: this.selectToday,
5117             scope: this
5118         });
5119         
5120         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5121             
5122         //});
5123         if (this.showClear) {
5124             
5125             baseTb.add( new Roo.Toolbar.Fill());
5126             baseTb.add({
5127                 text: '&#160;',
5128                 cls: 'x-btn-icon x-btn-clear',
5129                 handler: function() {
5130                     //this.value = '';
5131                     this.fireEvent("select", this, '');
5132                 },
5133                 scope: this
5134             });
5135         }
5136         
5137         
5138         if(Roo.isIE){
5139             this.el.repaint();
5140         }
5141         this.update(this.value);
5142     },
5143
5144     createMonthPicker : function(){
5145         if(!this.monthPicker.dom.firstChild){
5146             var buf = ['<table border="0" cellspacing="0">'];
5147             for(var i = 0; i < 6; i++){
5148                 buf.push(
5149                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5150                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5151                     i == 0 ?
5152                     '<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>' :
5153                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5154                 );
5155             }
5156             buf.push(
5157                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5158                     this.okText,
5159                     '</button><button type="button" class="x-date-mp-cancel">',
5160                     this.cancelText,
5161                     '</button></td></tr>',
5162                 '</table>'
5163             );
5164             this.monthPicker.update(buf.join(''));
5165             this.monthPicker.on('click', this.onMonthClick, this);
5166             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5167
5168             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5169             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5170
5171             this.mpMonths.each(function(m, a, i){
5172                 i += 1;
5173                 if((i%2) == 0){
5174                     m.dom.xmonth = 5 + Math.round(i * .5);
5175                 }else{
5176                     m.dom.xmonth = Math.round((i-1) * .5);
5177                 }
5178             });
5179         }
5180     },
5181
5182     showMonthPicker : function(){
5183         this.createMonthPicker();
5184         var size = this.el.getSize();
5185         this.monthPicker.setSize(size);
5186         this.monthPicker.child('table').setSize(size);
5187
5188         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5189         this.updateMPMonth(this.mpSelMonth);
5190         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5191         this.updateMPYear(this.mpSelYear);
5192
5193         this.monthPicker.slideIn('t', {duration:.2});
5194     },
5195
5196     updateMPYear : function(y){
5197         this.mpyear = y;
5198         var ys = this.mpYears.elements;
5199         for(var i = 1; i <= 10; i++){
5200             var td = ys[i-1], y2;
5201             if((i%2) == 0){
5202                 y2 = y + Math.round(i * .5);
5203                 td.firstChild.innerHTML = y2;
5204                 td.xyear = y2;
5205             }else{
5206                 y2 = y - (5-Math.round(i * .5));
5207                 td.firstChild.innerHTML = y2;
5208                 td.xyear = y2;
5209             }
5210             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5211         }
5212     },
5213
5214     updateMPMonth : function(sm){
5215         this.mpMonths.each(function(m, a, i){
5216             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5217         });
5218     },
5219
5220     selectMPMonth: function(m){
5221         
5222     },
5223
5224     onMonthClick : function(e, t){
5225         e.stopEvent();
5226         var el = new Roo.Element(t), pn;
5227         if(el.is('button.x-date-mp-cancel')){
5228             this.hideMonthPicker();
5229         }
5230         else if(el.is('button.x-date-mp-ok')){
5231             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5232             this.hideMonthPicker();
5233         }
5234         else if(pn = el.up('td.x-date-mp-month', 2)){
5235             this.mpMonths.removeClass('x-date-mp-sel');
5236             pn.addClass('x-date-mp-sel');
5237             this.mpSelMonth = pn.dom.xmonth;
5238         }
5239         else if(pn = el.up('td.x-date-mp-year', 2)){
5240             this.mpYears.removeClass('x-date-mp-sel');
5241             pn.addClass('x-date-mp-sel');
5242             this.mpSelYear = pn.dom.xyear;
5243         }
5244         else if(el.is('a.x-date-mp-prev')){
5245             this.updateMPYear(this.mpyear-10);
5246         }
5247         else if(el.is('a.x-date-mp-next')){
5248             this.updateMPYear(this.mpyear+10);
5249         }
5250     },
5251
5252     onMonthDblClick : function(e, t){
5253         e.stopEvent();
5254         var el = new Roo.Element(t), pn;
5255         if(pn = el.up('td.x-date-mp-month', 2)){
5256             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5257             this.hideMonthPicker();
5258         }
5259         else if(pn = el.up('td.x-date-mp-year', 2)){
5260             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5261             this.hideMonthPicker();
5262         }
5263     },
5264
5265     hideMonthPicker : function(disableAnim){
5266         if(this.monthPicker){
5267             if(disableAnim === true){
5268                 this.monthPicker.hide();
5269             }else{
5270                 this.monthPicker.slideOut('t', {duration:.2});
5271             }
5272         }
5273     },
5274
5275     // private
5276     showPrevMonth : function(e){
5277         this.update(this.activeDate.add("mo", -1));
5278     },
5279
5280     // private
5281     showNextMonth : function(e){
5282         this.update(this.activeDate.add("mo", 1));
5283     },
5284
5285     // private
5286     showPrevYear : function(){
5287         this.update(this.activeDate.add("y", -1));
5288     },
5289
5290     // private
5291     showNextYear : function(){
5292         this.update(this.activeDate.add("y", 1));
5293     },
5294
5295     // private
5296     handleMouseWheel : function(e){
5297         var delta = e.getWheelDelta();
5298         if(delta > 0){
5299             this.showPrevMonth();
5300             e.stopEvent();
5301         } else if(delta < 0){
5302             this.showNextMonth();
5303             e.stopEvent();
5304         }
5305     },
5306
5307     // private
5308     handleDateClick : function(e, t){
5309         e.stopEvent();
5310         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5311             this.setValue(new Date(t.dateValue));
5312             this.fireEvent("select", this, this.value);
5313         }
5314     },
5315
5316     // private
5317     selectToday : function(){
5318         this.setValue(new Date().clearTime());
5319         this.fireEvent("select", this, this.value);
5320     },
5321
5322     // private
5323     update : function(date)
5324     {
5325         var vd = this.activeDate;
5326         this.activeDate = date;
5327         if(vd && this.el){
5328             var t = date.getTime();
5329             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5330                 this.cells.removeClass("x-date-selected");
5331                 this.cells.each(function(c){
5332                    if(c.dom.firstChild.dateValue == t){
5333                        c.addClass("x-date-selected");
5334                        setTimeout(function(){
5335                             try{c.dom.firstChild.focus();}catch(e){}
5336                        }, 50);
5337                        return false;
5338                    }
5339                 });
5340                 return;
5341             }
5342         }
5343         
5344         var days = date.getDaysInMonth();
5345         var firstOfMonth = date.getFirstDateOfMonth();
5346         var startingPos = firstOfMonth.getDay()-this.startDay;
5347
5348         if(startingPos <= this.startDay){
5349             startingPos += 7;
5350         }
5351
5352         var pm = date.add("mo", -1);
5353         var prevStart = pm.getDaysInMonth()-startingPos;
5354
5355         var cells = this.cells.elements;
5356         var textEls = this.textNodes;
5357         days += startingPos;
5358
5359         // convert everything to numbers so it's fast
5360         var day = 86400000;
5361         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5362         var today = new Date().clearTime().getTime();
5363         var sel = date.clearTime().getTime();
5364         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5365         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5366         var ddMatch = this.disabledDatesRE;
5367         var ddText = this.disabledDatesText;
5368         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5369         var ddaysText = this.disabledDaysText;
5370         var format = this.format;
5371
5372         var setCellClass = function(cal, cell){
5373             cell.title = "";
5374             var t = d.getTime();
5375             cell.firstChild.dateValue = t;
5376             if(t == today){
5377                 cell.className += " x-date-today";
5378                 cell.title = cal.todayText;
5379             }
5380             if(t == sel){
5381                 cell.className += " x-date-selected";
5382                 setTimeout(function(){
5383                     try{cell.firstChild.focus();}catch(e){}
5384                 }, 50);
5385             }
5386             // disabling
5387             if(t < min) {
5388                 cell.className = " x-date-disabled";
5389                 cell.title = cal.minText;
5390                 return;
5391             }
5392             if(t > max) {
5393                 cell.className = " x-date-disabled";
5394                 cell.title = cal.maxText;
5395                 return;
5396             }
5397             if(ddays){
5398                 if(ddays.indexOf(d.getDay()) != -1){
5399                     cell.title = ddaysText;
5400                     cell.className = " x-date-disabled";
5401                 }
5402             }
5403             if(ddMatch && format){
5404                 var fvalue = d.dateFormat(format);
5405                 if(ddMatch.test(fvalue)){
5406                     cell.title = ddText.replace("%0", fvalue);
5407                     cell.className = " x-date-disabled";
5408                 }
5409             }
5410         };
5411
5412         var i = 0;
5413         for(; i < startingPos; i++) {
5414             textEls[i].innerHTML = (++prevStart);
5415             d.setDate(d.getDate()+1);
5416             cells[i].className = "x-date-prevday";
5417             setCellClass(this, cells[i]);
5418         }
5419         for(; i < days; i++){
5420             intDay = i - startingPos + 1;
5421             textEls[i].innerHTML = (intDay);
5422             d.setDate(d.getDate()+1);
5423             cells[i].className = "x-date-active";
5424             setCellClass(this, cells[i]);
5425         }
5426         var extraDays = 0;
5427         for(; i < 42; i++) {
5428              textEls[i].innerHTML = (++extraDays);
5429              d.setDate(d.getDate()+1);
5430              cells[i].className = "x-date-nextday";
5431              setCellClass(this, cells[i]);
5432         }
5433
5434         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5435         this.fireEvent('monthchange', this, date);
5436         
5437         if(!this.internalRender){
5438             var main = this.el.dom.firstChild;
5439             var w = main.offsetWidth;
5440             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5441             Roo.fly(main).setWidth(w);
5442             this.internalRender = true;
5443             // opera does not respect the auto grow header center column
5444             // then, after it gets a width opera refuses to recalculate
5445             // without a second pass
5446             if(Roo.isOpera && !this.secondPass){
5447                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5448                 this.secondPass = true;
5449                 this.update.defer(10, this, [date]);
5450             }
5451         }
5452         
5453         
5454     }
5455 });        /*
5456  * Based on:
5457  * Ext JS Library 1.1.1
5458  * Copyright(c) 2006-2007, Ext JS, LLC.
5459  *
5460  * Originally Released Under LGPL - original licence link has changed is not relivant.
5461  *
5462  * Fork - LGPL
5463  * <script type="text/javascript">
5464  */
5465 /**
5466  * @class Roo.TabPanel
5467  * @extends Roo.util.Observable
5468  * A lightweight tab container.
5469  * <br><br>
5470  * Usage:
5471  * <pre><code>
5472 // basic tabs 1, built from existing content
5473 var tabs = new Roo.TabPanel("tabs1");
5474 tabs.addTab("script", "View Script");
5475 tabs.addTab("markup", "View Markup");
5476 tabs.activate("script");
5477
5478 // more advanced tabs, built from javascript
5479 var jtabs = new Roo.TabPanel("jtabs");
5480 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5481
5482 // set up the UpdateManager
5483 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5484 var updater = tab2.getUpdateManager();
5485 updater.setDefaultUrl("ajax1.htm");
5486 tab2.on('activate', updater.refresh, updater, true);
5487
5488 // Use setUrl for Ajax loading
5489 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5490 tab3.setUrl("ajax2.htm", null, true);
5491
5492 // Disabled tab
5493 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5494 tab4.disable();
5495
5496 jtabs.activate("jtabs-1");
5497  * </code></pre>
5498  * @constructor
5499  * Create a new TabPanel.
5500  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5501  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5502  */
5503 Roo.TabPanel = function(container, config){
5504     /**
5505     * The container element for this TabPanel.
5506     * @type Roo.Element
5507     */
5508     this.el = Roo.get(container, true);
5509     if(config){
5510         if(typeof config == "boolean"){
5511             this.tabPosition = config ? "bottom" : "top";
5512         }else{
5513             Roo.apply(this, config);
5514         }
5515     }
5516     if(this.tabPosition == "bottom"){
5517         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5518         this.el.addClass("x-tabs-bottom");
5519     }
5520     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5521     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5522     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5523     if(Roo.isIE){
5524         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5525     }
5526     if(this.tabPosition != "bottom"){
5527         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5528          * @type Roo.Element
5529          */
5530         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5531         this.el.addClass("x-tabs-top");
5532     }
5533     this.items = [];
5534
5535     this.bodyEl.setStyle("position", "relative");
5536
5537     this.active = null;
5538     this.activateDelegate = this.activate.createDelegate(this);
5539
5540     this.addEvents({
5541         /**
5542          * @event tabchange
5543          * Fires when the active tab changes
5544          * @param {Roo.TabPanel} this
5545          * @param {Roo.TabPanelItem} activePanel The new active tab
5546          */
5547         "tabchange": true,
5548         /**
5549          * @event beforetabchange
5550          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5551          * @param {Roo.TabPanel} this
5552          * @param {Object} e Set cancel to true on this object to cancel the tab change
5553          * @param {Roo.TabPanelItem} tab The tab being changed to
5554          */
5555         "beforetabchange" : true
5556     });
5557
5558     Roo.EventManager.onWindowResize(this.onResize, this);
5559     this.cpad = this.el.getPadding("lr");
5560     this.hiddenCount = 0;
5561
5562
5563     // toolbar on the tabbar support...
5564     if (this.toolbar) {
5565         var tcfg = this.toolbar;
5566         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5567         this.toolbar = new Roo.Toolbar(tcfg);
5568         if (Roo.isSafari) {
5569             var tbl = tcfg.container.child('table', true);
5570             tbl.setAttribute('width', '100%');
5571         }
5572         
5573     }
5574    
5575
5576
5577     Roo.TabPanel.superclass.constructor.call(this);
5578 };
5579
5580 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5581     /*
5582      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5583      */
5584     tabPosition : "top",
5585     /*
5586      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5587      */
5588     currentTabWidth : 0,
5589     /*
5590      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5591      */
5592     minTabWidth : 40,
5593     /*
5594      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5595      */
5596     maxTabWidth : 250,
5597     /*
5598      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5599      */
5600     preferredTabWidth : 175,
5601     /*
5602      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5603      */
5604     resizeTabs : false,
5605     /*
5606      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5607      */
5608     monitorResize : true,
5609     /*
5610      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
5611      */
5612     toolbar : false,
5613
5614     /**
5615      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5616      * @param {String} id The id of the div to use <b>or create</b>
5617      * @param {String} text The text for the tab
5618      * @param {String} content (optional) Content to put in the TabPanelItem body
5619      * @param {Boolean} closable (optional) True to create a close icon on the tab
5620      * @return {Roo.TabPanelItem} The created TabPanelItem
5621      */
5622     addTab : function(id, text, content, closable){
5623         var item = new Roo.TabPanelItem(this, id, text, closable);
5624         this.addTabItem(item);
5625         if(content){
5626             item.setContent(content);
5627         }
5628         return item;
5629     },
5630
5631     /**
5632      * Returns the {@link Roo.TabPanelItem} with the specified id/index
5633      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5634      * @return {Roo.TabPanelItem}
5635      */
5636     getTab : function(id){
5637         return this.items[id];
5638     },
5639
5640     /**
5641      * Hides the {@link Roo.TabPanelItem} with the specified id/index
5642      * @param {String/Number} id The id or index of the TabPanelItem to hide.
5643      */
5644     hideTab : function(id){
5645         var t = this.items[id];
5646         if(!t.isHidden()){
5647            t.setHidden(true);
5648            this.hiddenCount++;
5649            this.autoSizeTabs();
5650         }
5651     },
5652
5653     /**
5654      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5655      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5656      */
5657     unhideTab : function(id){
5658         var t = this.items[id];
5659         if(t.isHidden()){
5660            t.setHidden(false);
5661            this.hiddenCount--;
5662            this.autoSizeTabs();
5663         }
5664     },
5665
5666     /**
5667      * Adds an existing {@link Roo.TabPanelItem}.
5668      * @param {Roo.TabPanelItem} item The TabPanelItem to add
5669      */
5670     addTabItem : function(item){
5671         this.items[item.id] = item;
5672         this.items.push(item);
5673         if(this.resizeTabs){
5674            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5675            this.autoSizeTabs();
5676         }else{
5677             item.autoSize();
5678         }
5679     },
5680
5681     /**
5682      * Removes a {@link Roo.TabPanelItem}.
5683      * @param {String/Number} id The id or index of the TabPanelItem to remove.
5684      */
5685     removeTab : function(id){
5686         var items = this.items;
5687         var tab = items[id];
5688         if(!tab) { return; }
5689         var index = items.indexOf(tab);
5690         if(this.active == tab && items.length > 1){
5691             var newTab = this.getNextAvailable(index);
5692             if(newTab) {
5693                 newTab.activate();
5694             }
5695         }
5696         this.stripEl.dom.removeChild(tab.pnode.dom);
5697         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5698             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5699         }
5700         items.splice(index, 1);
5701         delete this.items[tab.id];
5702         tab.fireEvent("close", tab);
5703         tab.purgeListeners();
5704         this.autoSizeTabs();
5705     },
5706
5707     getNextAvailable : function(start){
5708         var items = this.items;
5709         var index = start;
5710         // look for a next tab that will slide over to
5711         // replace the one being removed
5712         while(index < items.length){
5713             var item = items[++index];
5714             if(item && !item.isHidden()){
5715                 return item;
5716             }
5717         }
5718         // if one isn't found select the previous tab (on the left)
5719         index = start;
5720         while(index >= 0){
5721             var item = items[--index];
5722             if(item && !item.isHidden()){
5723                 return item;
5724             }
5725         }
5726         return null;
5727     },
5728
5729     /**
5730      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5731      * @param {String/Number} id The id or index of the TabPanelItem to disable.
5732      */
5733     disableTab : function(id){
5734         var tab = this.items[id];
5735         if(tab && this.active != tab){
5736             tab.disable();
5737         }
5738     },
5739
5740     /**
5741      * Enables a {@link Roo.TabPanelItem} that is disabled.
5742      * @param {String/Number} id The id or index of the TabPanelItem to enable.
5743      */
5744     enableTab : function(id){
5745         var tab = this.items[id];
5746         tab.enable();
5747     },
5748
5749     /**
5750      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5751      * @param {String/Number} id The id or index of the TabPanelItem to activate.
5752      * @return {Roo.TabPanelItem} The TabPanelItem.
5753      */
5754     activate : function(id){
5755         var tab = this.items[id];
5756         if(!tab){
5757             return null;
5758         }
5759         if(tab == this.active || tab.disabled){
5760             return tab;
5761         }
5762         var e = {};
5763         this.fireEvent("beforetabchange", this, e, tab);
5764         if(e.cancel !== true && !tab.disabled){
5765             if(this.active){
5766                 this.active.hide();
5767             }
5768             this.active = this.items[id];
5769             this.active.show();
5770             this.fireEvent("tabchange", this, this.active);
5771         }
5772         return tab;
5773     },
5774
5775     /**
5776      * Gets the active {@link Roo.TabPanelItem}.
5777      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5778      */
5779     getActiveTab : function(){
5780         return this.active;
5781     },
5782
5783     /**
5784      * Updates the tab body element to fit the height of the container element
5785      * for overflow scrolling
5786      * @param {Number} targetHeight (optional) Override the starting height from the elements height
5787      */
5788     syncHeight : function(targetHeight){
5789         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5790         var bm = this.bodyEl.getMargins();
5791         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5792         this.bodyEl.setHeight(newHeight);
5793         return newHeight;
5794     },
5795
5796     onResize : function(){
5797         if(this.monitorResize){
5798             this.autoSizeTabs();
5799         }
5800     },
5801
5802     /**
5803      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5804      */
5805     beginUpdate : function(){
5806         this.updating = true;
5807     },
5808
5809     /**
5810      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5811      */
5812     endUpdate : function(){
5813         this.updating = false;
5814         this.autoSizeTabs();
5815     },
5816
5817     /**
5818      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5819      */
5820     autoSizeTabs : function(){
5821         var count = this.items.length;
5822         var vcount = count - this.hiddenCount;
5823         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5824             return;
5825         }
5826         var w = Math.max(this.el.getWidth() - this.cpad, 10);
5827         var availWidth = Math.floor(w / vcount);
5828         var b = this.stripBody;
5829         if(b.getWidth() > w){
5830             var tabs = this.items;
5831             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5832             if(availWidth < this.minTabWidth){
5833                 /*if(!this.sleft){    // incomplete scrolling code
5834                     this.createScrollButtons();
5835                 }
5836                 this.showScroll();
5837                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5838             }
5839         }else{
5840             if(this.currentTabWidth < this.preferredTabWidth){
5841                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5842             }
5843         }
5844     },
5845
5846     /**
5847      * Returns the number of tabs in this TabPanel.
5848      * @return {Number}
5849      */
5850      getCount : function(){
5851          return this.items.length;
5852      },
5853
5854     /**
5855      * Resizes all the tabs to the passed width
5856      * @param {Number} The new width
5857      */
5858     setTabWidth : function(width){
5859         this.currentTabWidth = width;
5860         for(var i = 0, len = this.items.length; i < len; i++) {
5861                 if(!this.items[i].isHidden()) {
5862                 this.items[i].setWidth(width);
5863             }
5864         }
5865     },
5866
5867     /**
5868      * Destroys this TabPanel
5869      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5870      */
5871     destroy : function(removeEl){
5872         Roo.EventManager.removeResizeListener(this.onResize, this);
5873         for(var i = 0, len = this.items.length; i < len; i++){
5874             this.items[i].purgeListeners();
5875         }
5876         if(removeEl === true){
5877             this.el.update("");
5878             this.el.remove();
5879         }
5880     }
5881 });
5882
5883 /**
5884  * @class Roo.TabPanelItem
5885  * @extends Roo.util.Observable
5886  * Represents an individual item (tab plus body) in a TabPanel.
5887  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5888  * @param {String} id The id of this TabPanelItem
5889  * @param {String} text The text for the tab of this TabPanelItem
5890  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5891  */
5892 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5893     /**
5894      * The {@link Roo.TabPanel} this TabPanelItem belongs to
5895      * @type Roo.TabPanel
5896      */
5897     this.tabPanel = tabPanel;
5898     /**
5899      * The id for this TabPanelItem
5900      * @type String
5901      */
5902     this.id = id;
5903     /** @private */
5904     this.disabled = false;
5905     /** @private */
5906     this.text = text;
5907     /** @private */
5908     this.loaded = false;
5909     this.closable = closable;
5910
5911     /**
5912      * The body element for this TabPanelItem.
5913      * @type Roo.Element
5914      */
5915     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5916     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5917     this.bodyEl.setStyle("display", "block");
5918     this.bodyEl.setStyle("zoom", "1");
5919     this.hideAction();
5920
5921     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5922     /** @private */
5923     this.el = Roo.get(els.el, true);
5924     this.inner = Roo.get(els.inner, true);
5925     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5926     this.pnode = Roo.get(els.el.parentNode, true);
5927     this.el.on("mousedown", this.onTabMouseDown, this);
5928     this.el.on("click", this.onTabClick, this);
5929     /** @private */
5930     if(closable){
5931         var c = Roo.get(els.close, true);
5932         c.dom.title = this.closeText;
5933         c.addClassOnOver("close-over");
5934         c.on("click", this.closeClick, this);
5935      }
5936
5937     this.addEvents({
5938          /**
5939          * @event activate
5940          * Fires when this tab becomes the active tab.
5941          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5942          * @param {Roo.TabPanelItem} this
5943          */
5944         "activate": true,
5945         /**
5946          * @event beforeclose
5947          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5948          * @param {Roo.TabPanelItem} this
5949          * @param {Object} e Set cancel to true on this object to cancel the close.
5950          */
5951         "beforeclose": true,
5952         /**
5953          * @event close
5954          * Fires when this tab is closed.
5955          * @param {Roo.TabPanelItem} this
5956          */
5957          "close": true,
5958         /**
5959          * @event deactivate
5960          * Fires when this tab is no longer the active tab.
5961          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5962          * @param {Roo.TabPanelItem} this
5963          */
5964          "deactivate" : true
5965     });
5966     this.hidden = false;
5967
5968     Roo.TabPanelItem.superclass.constructor.call(this);
5969 };
5970
5971 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5972     purgeListeners : function(){
5973        Roo.util.Observable.prototype.purgeListeners.call(this);
5974        this.el.removeAllListeners();
5975     },
5976     /**
5977      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5978      */
5979     show : function(){
5980         this.pnode.addClass("on");
5981         this.showAction();
5982         if(Roo.isOpera){
5983             this.tabPanel.stripWrap.repaint();
5984         }
5985         this.fireEvent("activate", this.tabPanel, this);
5986     },
5987
5988     /**
5989      * Returns true if this tab is the active tab.
5990      * @return {Boolean}
5991      */
5992     isActive : function(){
5993         return this.tabPanel.getActiveTab() == this;
5994     },
5995
5996     /**
5997      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
5998      */
5999     hide : function(){
6000         this.pnode.removeClass("on");
6001         this.hideAction();
6002         this.fireEvent("deactivate", this.tabPanel, this);
6003     },
6004
6005     hideAction : function(){
6006         this.bodyEl.hide();
6007         this.bodyEl.setStyle("position", "absolute");
6008         this.bodyEl.setLeft("-20000px");
6009         this.bodyEl.setTop("-20000px");
6010     },
6011
6012     showAction : function(){
6013         this.bodyEl.setStyle("position", "relative");
6014         this.bodyEl.setTop("");
6015         this.bodyEl.setLeft("");
6016         this.bodyEl.show();
6017     },
6018
6019     /**
6020      * Set the tooltip for the tab.
6021      * @param {String} tooltip The tab's tooltip
6022      */
6023     setTooltip : function(text){
6024         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6025             this.textEl.dom.qtip = text;
6026             this.textEl.dom.removeAttribute('title');
6027         }else{
6028             this.textEl.dom.title = text;
6029         }
6030     },
6031
6032     onTabClick : function(e){
6033         e.preventDefault();
6034         this.tabPanel.activate(this.id);
6035     },
6036
6037     onTabMouseDown : function(e){
6038         e.preventDefault();
6039         this.tabPanel.activate(this.id);
6040     },
6041
6042     getWidth : function(){
6043         return this.inner.getWidth();
6044     },
6045
6046     setWidth : function(width){
6047         var iwidth = width - this.pnode.getPadding("lr");
6048         this.inner.setWidth(iwidth);
6049         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6050         this.pnode.setWidth(width);
6051     },
6052
6053     /**
6054      * Show or hide the tab
6055      * @param {Boolean} hidden True to hide or false to show.
6056      */
6057     setHidden : function(hidden){
6058         this.hidden = hidden;
6059         this.pnode.setStyle("display", hidden ? "none" : "");
6060     },
6061
6062     /**
6063      * Returns true if this tab is "hidden"
6064      * @return {Boolean}
6065      */
6066     isHidden : function(){
6067         return this.hidden;
6068     },
6069
6070     /**
6071      * Returns the text for this tab
6072      * @return {String}
6073      */
6074     getText : function(){
6075         return this.text;
6076     },
6077
6078     autoSize : function(){
6079         //this.el.beginMeasure();
6080         this.textEl.setWidth(1);
6081         /*
6082          *  #2804 [new] Tabs in Roojs
6083          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6084          */
6085         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6086         //this.el.endMeasure();
6087     },
6088
6089     /**
6090      * Sets the text for the tab (Note: this also sets the tooltip text)
6091      * @param {String} text The tab's text and tooltip
6092      */
6093     setText : function(text){
6094         this.text = text;
6095         this.textEl.update(text);
6096         this.setTooltip(text);
6097         if(!this.tabPanel.resizeTabs){
6098             this.autoSize();
6099         }
6100     },
6101     /**
6102      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6103      */
6104     activate : function(){
6105         this.tabPanel.activate(this.id);
6106     },
6107
6108     /**
6109      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6110      */
6111     disable : function(){
6112         if(this.tabPanel.active != this){
6113             this.disabled = true;
6114             this.pnode.addClass("disabled");
6115         }
6116     },
6117
6118     /**
6119      * Enables this TabPanelItem if it was previously disabled.
6120      */
6121     enable : function(){
6122         this.disabled = false;
6123         this.pnode.removeClass("disabled");
6124     },
6125
6126     /**
6127      * Sets the content for this TabPanelItem.
6128      * @param {String} content The content
6129      * @param {Boolean} loadScripts true to look for and load scripts
6130      */
6131     setContent : function(content, loadScripts){
6132         this.bodyEl.update(content, loadScripts);
6133     },
6134
6135     /**
6136      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6137      * @return {Roo.UpdateManager} The UpdateManager
6138      */
6139     getUpdateManager : function(){
6140         return this.bodyEl.getUpdateManager();
6141     },
6142
6143     /**
6144      * Set a URL to be used to load the content for this TabPanelItem.
6145      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6146      * @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)
6147      * @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)
6148      * @return {Roo.UpdateManager} The UpdateManager
6149      */
6150     setUrl : function(url, params, loadOnce){
6151         if(this.refreshDelegate){
6152             this.un('activate', this.refreshDelegate);
6153         }
6154         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6155         this.on("activate", this.refreshDelegate);
6156         return this.bodyEl.getUpdateManager();
6157     },
6158
6159     /** @private */
6160     _handleRefresh : function(url, params, loadOnce){
6161         if(!loadOnce || !this.loaded){
6162             var updater = this.bodyEl.getUpdateManager();
6163             updater.update(url, params, this._setLoaded.createDelegate(this));
6164         }
6165     },
6166
6167     /**
6168      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6169      *   Will fail silently if the setUrl method has not been called.
6170      *   This does not activate the panel, just updates its content.
6171      */
6172     refresh : function(){
6173         if(this.refreshDelegate){
6174            this.loaded = false;
6175            this.refreshDelegate();
6176         }
6177     },
6178
6179     /** @private */
6180     _setLoaded : function(){
6181         this.loaded = true;
6182     },
6183
6184     /** @private */
6185     closeClick : function(e){
6186         var o = {};
6187         e.stopEvent();
6188         this.fireEvent("beforeclose", this, o);
6189         if(o.cancel !== true){
6190             this.tabPanel.removeTab(this.id);
6191         }
6192     },
6193     /**
6194      * The text displayed in the tooltip for the close icon.
6195      * @type String
6196      */
6197     closeText : "Close this tab"
6198 });
6199
6200 /** @private */
6201 Roo.TabPanel.prototype.createStrip = function(container){
6202     var strip = document.createElement("div");
6203     strip.className = "x-tabs-wrap";
6204     container.appendChild(strip);
6205     return strip;
6206 };
6207 /** @private */
6208 Roo.TabPanel.prototype.createStripList = function(strip){
6209     // div wrapper for retard IE
6210     // returns the "tr" element.
6211     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6212         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6213         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6214     return strip.firstChild.firstChild.firstChild.firstChild;
6215 };
6216 /** @private */
6217 Roo.TabPanel.prototype.createBody = function(container){
6218     var body = document.createElement("div");
6219     Roo.id(body, "tab-body");
6220     Roo.fly(body).addClass("x-tabs-body");
6221     container.appendChild(body);
6222     return body;
6223 };
6224 /** @private */
6225 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6226     var body = Roo.getDom(id);
6227     if(!body){
6228         body = document.createElement("div");
6229         body.id = id;
6230     }
6231     Roo.fly(body).addClass("x-tabs-item-body");
6232     bodyEl.insertBefore(body, bodyEl.firstChild);
6233     return body;
6234 };
6235 /** @private */
6236 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6237     var td = document.createElement("td");
6238     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6239     //stripEl.appendChild(td);
6240     if(closable){
6241         td.className = "x-tabs-closable";
6242         if(!this.closeTpl){
6243             this.closeTpl = new Roo.Template(
6244                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6245                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6246                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6247             );
6248         }
6249         var el = this.closeTpl.overwrite(td, {"text": text});
6250         var close = el.getElementsByTagName("div")[0];
6251         var inner = el.getElementsByTagName("em")[0];
6252         return {"el": el, "close": close, "inner": inner};
6253     } else {
6254         if(!this.tabTpl){
6255             this.tabTpl = new Roo.Template(
6256                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6257                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6258             );
6259         }
6260         var el = this.tabTpl.overwrite(td, {"text": text});
6261         var inner = el.getElementsByTagName("em")[0];
6262         return {"el": el, "inner": inner};
6263     }
6264 };/*
6265  * Based on:
6266  * Ext JS Library 1.1.1
6267  * Copyright(c) 2006-2007, Ext JS, LLC.
6268  *
6269  * Originally Released Under LGPL - original licence link has changed is not relivant.
6270  *
6271  * Fork - LGPL
6272  * <script type="text/javascript">
6273  */
6274
6275 /**
6276  * @class Roo.Button
6277  * @extends Roo.util.Observable
6278  * Simple Button class
6279  * @cfg {String} text The button text
6280  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6281  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6282  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6283  * @cfg {Object} scope The scope of the handler
6284  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6285  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6286  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6287  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6288  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6289  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6290    applies if enableToggle = true)
6291  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6292  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6293   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6294  * @constructor
6295  * Create a new button
6296  * @param {Object} config The config object
6297  */
6298 Roo.Button = function(renderTo, config)
6299 {
6300     if (!config) {
6301         config = renderTo;
6302         renderTo = config.renderTo || false;
6303     }
6304     
6305     Roo.apply(this, config);
6306     this.addEvents({
6307         /**
6308              * @event click
6309              * Fires when this button is clicked
6310              * @param {Button} this
6311              * @param {EventObject} e The click event
6312              */
6313             "click" : true,
6314         /**
6315              * @event toggle
6316              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6317              * @param {Button} this
6318              * @param {Boolean} pressed
6319              */
6320             "toggle" : true,
6321         /**
6322              * @event mouseover
6323              * Fires when the mouse hovers over the button
6324              * @param {Button} this
6325              * @param {Event} e The event object
6326              */
6327         'mouseover' : true,
6328         /**
6329              * @event mouseout
6330              * Fires when the mouse exits the button
6331              * @param {Button} this
6332              * @param {Event} e The event object
6333              */
6334         'mouseout': true,
6335          /**
6336              * @event render
6337              * Fires when the button is rendered
6338              * @param {Button} this
6339              */
6340         'render': true
6341     });
6342     if(this.menu){
6343         this.menu = Roo.menu.MenuMgr.get(this.menu);
6344     }
6345     // register listeners first!!  - so render can be captured..
6346     Roo.util.Observable.call(this);
6347     if(renderTo){
6348         this.render(renderTo);
6349     }
6350     
6351   
6352 };
6353
6354 Roo.extend(Roo.Button, Roo.util.Observable, {
6355     /**
6356      * 
6357      */
6358     
6359     /**
6360      * Read-only. True if this button is hidden
6361      * @type Boolean
6362      */
6363     hidden : false,
6364     /**
6365      * Read-only. True if this button is disabled
6366      * @type Boolean
6367      */
6368     disabled : false,
6369     /**
6370      * Read-only. True if this button is pressed (only if enableToggle = true)
6371      * @type Boolean
6372      */
6373     pressed : false,
6374
6375     /**
6376      * @cfg {Number} tabIndex 
6377      * The DOM tabIndex for this button (defaults to undefined)
6378      */
6379     tabIndex : undefined,
6380
6381     /**
6382      * @cfg {Boolean} enableToggle
6383      * True to enable pressed/not pressed toggling (defaults to false)
6384      */
6385     enableToggle: false,
6386     /**
6387      * @cfg {Roo.menu.Menu} menu
6388      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6389      */
6390     menu : undefined,
6391     /**
6392      * @cfg {String} menuAlign
6393      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6394      */
6395     menuAlign : "tl-bl?",
6396
6397     /**
6398      * @cfg {String} iconCls
6399      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6400      */
6401     iconCls : undefined,
6402     /**
6403      * @cfg {String} type
6404      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6405      */
6406     type : 'button',
6407
6408     // private
6409     menuClassTarget: 'tr',
6410
6411     /**
6412      * @cfg {String} clickEvent
6413      * The type of event to map to the button's event handler (defaults to 'click')
6414      */
6415     clickEvent : 'click',
6416
6417     /**
6418      * @cfg {Boolean} handleMouseEvents
6419      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6420      */
6421     handleMouseEvents : true,
6422
6423     /**
6424      * @cfg {String} tooltipType
6425      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6426      */
6427     tooltipType : 'qtip',
6428
6429     /**
6430      * @cfg {String} cls
6431      * A CSS class to apply to the button's main element.
6432      */
6433     
6434     /**
6435      * @cfg {Roo.Template} template (Optional)
6436      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6437      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6438      * require code modifications if required elements (e.g. a button) aren't present.
6439      */
6440
6441     // private
6442     render : function(renderTo){
6443         var btn;
6444         if(this.hideParent){
6445             this.parentEl = Roo.get(renderTo);
6446         }
6447         if(!this.dhconfig){
6448             if(!this.template){
6449                 if(!Roo.Button.buttonTemplate){
6450                     // hideous table template
6451                     Roo.Button.buttonTemplate = new Roo.Template(
6452                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6453                         '<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>',
6454                         "</tr></tbody></table>");
6455                 }
6456                 this.template = Roo.Button.buttonTemplate;
6457             }
6458             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6459             var btnEl = btn.child("button:first");
6460             btnEl.on('focus', this.onFocus, this);
6461             btnEl.on('blur', this.onBlur, this);
6462             if(this.cls){
6463                 btn.addClass(this.cls);
6464             }
6465             if(this.icon){
6466                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6467             }
6468             if(this.iconCls){
6469                 btnEl.addClass(this.iconCls);
6470                 if(!this.cls){
6471                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6472                 }
6473             }
6474             if(this.tabIndex !== undefined){
6475                 btnEl.dom.tabIndex = this.tabIndex;
6476             }
6477             if(this.tooltip){
6478                 if(typeof this.tooltip == 'object'){
6479                     Roo.QuickTips.tips(Roo.apply({
6480                           target: btnEl.id
6481                     }, this.tooltip));
6482                 } else {
6483                     btnEl.dom[this.tooltipType] = this.tooltip;
6484                 }
6485             }
6486         }else{
6487             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6488         }
6489         this.el = btn;
6490         if(this.id){
6491             this.el.dom.id = this.el.id = this.id;
6492         }
6493         if(this.menu){
6494             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6495             this.menu.on("show", this.onMenuShow, this);
6496             this.menu.on("hide", this.onMenuHide, this);
6497         }
6498         btn.addClass("x-btn");
6499         if(Roo.isIE && !Roo.isIE7){
6500             this.autoWidth.defer(1, this);
6501         }else{
6502             this.autoWidth();
6503         }
6504         if(this.handleMouseEvents){
6505             btn.on("mouseover", this.onMouseOver, this);
6506             btn.on("mouseout", this.onMouseOut, this);
6507             btn.on("mousedown", this.onMouseDown, this);
6508         }
6509         btn.on(this.clickEvent, this.onClick, this);
6510         //btn.on("mouseup", this.onMouseUp, this);
6511         if(this.hidden){
6512             this.hide();
6513         }
6514         if(this.disabled){
6515             this.disable();
6516         }
6517         Roo.ButtonToggleMgr.register(this);
6518         if(this.pressed){
6519             this.el.addClass("x-btn-pressed");
6520         }
6521         if(this.repeat){
6522             var repeater = new Roo.util.ClickRepeater(btn,
6523                 typeof this.repeat == "object" ? this.repeat : {}
6524             );
6525             repeater.on("click", this.onClick,  this);
6526         }
6527         
6528         this.fireEvent('render', this);
6529         
6530     },
6531     /**
6532      * Returns the button's underlying element
6533      * @return {Roo.Element} The element
6534      */
6535     getEl : function(){
6536         return this.el;  
6537     },
6538     
6539     /**
6540      * Destroys this Button and removes any listeners.
6541      */
6542     destroy : function(){
6543         Roo.ButtonToggleMgr.unregister(this);
6544         this.el.removeAllListeners();
6545         this.purgeListeners();
6546         this.el.remove();
6547     },
6548
6549     // private
6550     autoWidth : function(){
6551         if(this.el){
6552             this.el.setWidth("auto");
6553             if(Roo.isIE7 && Roo.isStrict){
6554                 var ib = this.el.child('button');
6555                 if(ib && ib.getWidth() > 20){
6556                     ib.clip();
6557                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6558                 }
6559             }
6560             if(this.minWidth){
6561                 if(this.hidden){
6562                     this.el.beginMeasure();
6563                 }
6564                 if(this.el.getWidth() < this.minWidth){
6565                     this.el.setWidth(this.minWidth);
6566                 }
6567                 if(this.hidden){
6568                     this.el.endMeasure();
6569                 }
6570             }
6571         }
6572     },
6573
6574     /**
6575      * Assigns this button's click handler
6576      * @param {Function} handler The function to call when the button is clicked
6577      * @param {Object} scope (optional) Scope for the function passed in
6578      */
6579     setHandler : function(handler, scope){
6580         this.handler = handler;
6581         this.scope = scope;  
6582     },
6583     
6584     /**
6585      * Sets this button's text
6586      * @param {String} text The button text
6587      */
6588     setText : function(text){
6589         this.text = text;
6590         if(this.el){
6591             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6592         }
6593         this.autoWidth();
6594     },
6595     
6596     /**
6597      * Gets the text for this button
6598      * @return {String} The button text
6599      */
6600     getText : function(){
6601         return this.text;  
6602     },
6603     
6604     /**
6605      * Show this button
6606      */
6607     show: function(){
6608         this.hidden = false;
6609         if(this.el){
6610             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6611         }
6612     },
6613     
6614     /**
6615      * Hide this button
6616      */
6617     hide: function(){
6618         this.hidden = true;
6619         if(this.el){
6620             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6621         }
6622     },
6623     
6624     /**
6625      * Convenience function for boolean show/hide
6626      * @param {Boolean} visible True to show, false to hide
6627      */
6628     setVisible: function(visible){
6629         if(visible) {
6630             this.show();
6631         }else{
6632             this.hide();
6633         }
6634     },
6635     
6636     /**
6637      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6638      * @param {Boolean} state (optional) Force a particular state
6639      */
6640     toggle : function(state){
6641         state = state === undefined ? !this.pressed : state;
6642         if(state != this.pressed){
6643             if(state){
6644                 this.el.addClass("x-btn-pressed");
6645                 this.pressed = true;
6646                 this.fireEvent("toggle", this, true);
6647             }else{
6648                 this.el.removeClass("x-btn-pressed");
6649                 this.pressed = false;
6650                 this.fireEvent("toggle", this, false);
6651             }
6652             if(this.toggleHandler){
6653                 this.toggleHandler.call(this.scope || this, this, state);
6654             }
6655         }
6656     },
6657     
6658     /**
6659      * Focus the button
6660      */
6661     focus : function(){
6662         this.el.child('button:first').focus();
6663     },
6664     
6665     /**
6666      * Disable this button
6667      */
6668     disable : function(){
6669         if(this.el){
6670             this.el.addClass("x-btn-disabled");
6671         }
6672         this.disabled = true;
6673     },
6674     
6675     /**
6676      * Enable this button
6677      */
6678     enable : function(){
6679         if(this.el){
6680             this.el.removeClass("x-btn-disabled");
6681         }
6682         this.disabled = false;
6683     },
6684
6685     /**
6686      * Convenience function for boolean enable/disable
6687      * @param {Boolean} enabled True to enable, false to disable
6688      */
6689     setDisabled : function(v){
6690         this[v !== true ? "enable" : "disable"]();
6691     },
6692
6693     // private
6694     onClick : function(e)
6695     {
6696         if(e){
6697             e.preventDefault();
6698         }
6699         if(e.button != 0){
6700             return;
6701         }
6702         if(!this.disabled){
6703             if(this.enableToggle){
6704                 this.toggle();
6705             }
6706             if(this.menu && !this.menu.isVisible()){
6707                 this.menu.show(this.el, this.menuAlign);
6708             }
6709             this.fireEvent("click", this, e);
6710             if(this.handler){
6711                 this.el.removeClass("x-btn-over");
6712                 this.handler.call(this.scope || this, this, e);
6713             }
6714         }
6715     },
6716     // private
6717     onMouseOver : function(e){
6718         if(!this.disabled){
6719             this.el.addClass("x-btn-over");
6720             this.fireEvent('mouseover', this, e);
6721         }
6722     },
6723     // private
6724     onMouseOut : function(e){
6725         if(!e.within(this.el,  true)){
6726             this.el.removeClass("x-btn-over");
6727             this.fireEvent('mouseout', this, e);
6728         }
6729     },
6730     // private
6731     onFocus : function(e){
6732         if(!this.disabled){
6733             this.el.addClass("x-btn-focus");
6734         }
6735     },
6736     // private
6737     onBlur : function(e){
6738         this.el.removeClass("x-btn-focus");
6739     },
6740     // private
6741     onMouseDown : function(e){
6742         if(!this.disabled && e.button == 0){
6743             this.el.addClass("x-btn-click");
6744             Roo.get(document).on('mouseup', this.onMouseUp, this);
6745         }
6746     },
6747     // private
6748     onMouseUp : function(e){
6749         if(e.button == 0){
6750             this.el.removeClass("x-btn-click");
6751             Roo.get(document).un('mouseup', this.onMouseUp, this);
6752         }
6753     },
6754     // private
6755     onMenuShow : function(e){
6756         this.el.addClass("x-btn-menu-active");
6757     },
6758     // private
6759     onMenuHide : function(e){
6760         this.el.removeClass("x-btn-menu-active");
6761     }   
6762 });
6763
6764 // Private utility class used by Button
6765 Roo.ButtonToggleMgr = function(){
6766    var groups = {};
6767    
6768    function toggleGroup(btn, state){
6769        if(state){
6770            var g = groups[btn.toggleGroup];
6771            for(var i = 0, l = g.length; i < l; i++){
6772                if(g[i] != btn){
6773                    g[i].toggle(false);
6774                }
6775            }
6776        }
6777    }
6778    
6779    return {
6780        register : function(btn){
6781            if(!btn.toggleGroup){
6782                return;
6783            }
6784            var g = groups[btn.toggleGroup];
6785            if(!g){
6786                g = groups[btn.toggleGroup] = [];
6787            }
6788            g.push(btn);
6789            btn.on("toggle", toggleGroup);
6790        },
6791        
6792        unregister : function(btn){
6793            if(!btn.toggleGroup){
6794                return;
6795            }
6796            var g = groups[btn.toggleGroup];
6797            if(g){
6798                g.remove(btn);
6799                btn.un("toggle", toggleGroup);
6800            }
6801        }
6802    };
6803 }();/*
6804  * Based on:
6805  * Ext JS Library 1.1.1
6806  * Copyright(c) 2006-2007, Ext JS, LLC.
6807  *
6808  * Originally Released Under LGPL - original licence link has changed is not relivant.
6809  *
6810  * Fork - LGPL
6811  * <script type="text/javascript">
6812  */
6813  
6814 /**
6815  * @class Roo.SplitButton
6816  * @extends Roo.Button
6817  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6818  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
6819  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6820  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6821  * @cfg {String} arrowTooltip The title attribute of the arrow
6822  * @constructor
6823  * Create a new menu button
6824  * @param {String/HTMLElement/Element} renderTo The element to append the button to
6825  * @param {Object} config The config object
6826  */
6827 Roo.SplitButton = function(renderTo, config){
6828     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6829     /**
6830      * @event arrowclick
6831      * Fires when this button's arrow is clicked
6832      * @param {SplitButton} this
6833      * @param {EventObject} e The click event
6834      */
6835     this.addEvents({"arrowclick":true});
6836 };
6837
6838 Roo.extend(Roo.SplitButton, Roo.Button, {
6839     render : function(renderTo){
6840         // this is one sweet looking template!
6841         var tpl = new Roo.Template(
6842             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6843             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6844             '<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>',
6845             "</tbody></table></td><td>",
6846             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6847             '<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>',
6848             "</tbody></table></td></tr></table>"
6849         );
6850         var btn = tpl.append(renderTo, [this.text, this.type], true);
6851         var btnEl = btn.child("button");
6852         if(this.cls){
6853             btn.addClass(this.cls);
6854         }
6855         if(this.icon){
6856             btnEl.setStyle('background-image', 'url(' +this.icon +')');
6857         }
6858         if(this.iconCls){
6859             btnEl.addClass(this.iconCls);
6860             if(!this.cls){
6861                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6862             }
6863         }
6864         this.el = btn;
6865         if(this.handleMouseEvents){
6866             btn.on("mouseover", this.onMouseOver, this);
6867             btn.on("mouseout", this.onMouseOut, this);
6868             btn.on("mousedown", this.onMouseDown, this);
6869             btn.on("mouseup", this.onMouseUp, this);
6870         }
6871         btn.on(this.clickEvent, this.onClick, this);
6872         if(this.tooltip){
6873             if(typeof this.tooltip == 'object'){
6874                 Roo.QuickTips.tips(Roo.apply({
6875                       target: btnEl.id
6876                 }, this.tooltip));
6877             } else {
6878                 btnEl.dom[this.tooltipType] = this.tooltip;
6879             }
6880         }
6881         if(this.arrowTooltip){
6882             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6883         }
6884         if(this.hidden){
6885             this.hide();
6886         }
6887         if(this.disabled){
6888             this.disable();
6889         }
6890         if(this.pressed){
6891             this.el.addClass("x-btn-pressed");
6892         }
6893         if(Roo.isIE && !Roo.isIE7){
6894             this.autoWidth.defer(1, this);
6895         }else{
6896             this.autoWidth();
6897         }
6898         if(this.menu){
6899             this.menu.on("show", this.onMenuShow, this);
6900             this.menu.on("hide", this.onMenuHide, this);
6901         }
6902         this.fireEvent('render', this);
6903     },
6904
6905     // private
6906     autoWidth : function(){
6907         if(this.el){
6908             var tbl = this.el.child("table:first");
6909             var tbl2 = this.el.child("table:last");
6910             this.el.setWidth("auto");
6911             tbl.setWidth("auto");
6912             if(Roo.isIE7 && Roo.isStrict){
6913                 var ib = this.el.child('button:first');
6914                 if(ib && ib.getWidth() > 20){
6915                     ib.clip();
6916                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6917                 }
6918             }
6919             if(this.minWidth){
6920                 if(this.hidden){
6921                     this.el.beginMeasure();
6922                 }
6923                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6924                     tbl.setWidth(this.minWidth-tbl2.getWidth());
6925                 }
6926                 if(this.hidden){
6927                     this.el.endMeasure();
6928                 }
6929             }
6930             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6931         } 
6932     },
6933     /**
6934      * Sets this button's click handler
6935      * @param {Function} handler The function to call when the button is clicked
6936      * @param {Object} scope (optional) Scope for the function passed above
6937      */
6938     setHandler : function(handler, scope){
6939         this.handler = handler;
6940         this.scope = scope;  
6941     },
6942     
6943     /**
6944      * Sets this button's arrow click handler
6945      * @param {Function} handler The function to call when the arrow is clicked
6946      * @param {Object} scope (optional) Scope for the function passed above
6947      */
6948     setArrowHandler : function(handler, scope){
6949         this.arrowHandler = handler;
6950         this.scope = scope;  
6951     },
6952     
6953     /**
6954      * Focus the button
6955      */
6956     focus : function(){
6957         if(this.el){
6958             this.el.child("button:first").focus();
6959         }
6960     },
6961
6962     // private
6963     onClick : function(e){
6964         e.preventDefault();
6965         if(!this.disabled){
6966             if(e.getTarget(".x-btn-menu-arrow-wrap")){
6967                 if(this.menu && !this.menu.isVisible()){
6968                     this.menu.show(this.el, this.menuAlign);
6969                 }
6970                 this.fireEvent("arrowclick", this, e);
6971                 if(this.arrowHandler){
6972                     this.arrowHandler.call(this.scope || this, this, e);
6973                 }
6974             }else{
6975                 this.fireEvent("click", this, e);
6976                 if(this.handler){
6977                     this.handler.call(this.scope || this, this, e);
6978                 }
6979             }
6980         }
6981     },
6982     // private
6983     onMouseDown : function(e){
6984         if(!this.disabled){
6985             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
6986         }
6987     },
6988     // private
6989     onMouseUp : function(e){
6990         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
6991     }   
6992 });
6993
6994
6995 // backwards compat
6996 Roo.MenuButton = Roo.SplitButton;/*
6997  * Based on:
6998  * Ext JS Library 1.1.1
6999  * Copyright(c) 2006-2007, Ext JS, LLC.
7000  *
7001  * Originally Released Under LGPL - original licence link has changed is not relivant.
7002  *
7003  * Fork - LGPL
7004  * <script type="text/javascript">
7005  */
7006
7007 /**
7008  * @class Roo.Toolbar
7009  * @children   Roo.Toolbar.Item Roo.form.Field
7010  * Basic Toolbar class.
7011  * @constructor
7012  * Creates a new Toolbar
7013  * @param {Object} container The config object
7014  */ 
7015 Roo.Toolbar = function(container, buttons, config)
7016 {
7017     /// old consturctor format still supported..
7018     if(container instanceof Array){ // omit the container for later rendering
7019         buttons = container;
7020         config = buttons;
7021         container = null;
7022     }
7023     if (typeof(container) == 'object' && container.xtype) {
7024         config = container;
7025         container = config.container;
7026         buttons = config.buttons || []; // not really - use items!!
7027     }
7028     var xitems = [];
7029     if (config && config.items) {
7030         xitems = config.items;
7031         delete config.items;
7032     }
7033     Roo.apply(this, config);
7034     this.buttons = buttons;
7035     
7036     if(container){
7037         this.render(container);
7038     }
7039     this.xitems = xitems;
7040     Roo.each(xitems, function(b) {
7041         this.add(b);
7042     }, this);
7043     
7044 };
7045
7046 Roo.Toolbar.prototype = {
7047     /**
7048      * @cfg {Array} items
7049      * array of button configs or elements to add (will be converted to a MixedCollection)
7050      */
7051     items: false,
7052     /**
7053      * @cfg {String/HTMLElement/Element} container
7054      * The id or element that will contain the toolbar
7055      */
7056     // private
7057     render : function(ct){
7058         this.el = Roo.get(ct);
7059         if(this.cls){
7060             this.el.addClass(this.cls);
7061         }
7062         // using a table allows for vertical alignment
7063         // 100% width is needed by Safari...
7064         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7065         this.tr = this.el.child("tr", true);
7066         var autoId = 0;
7067         this.items = new Roo.util.MixedCollection(false, function(o){
7068             return o.id || ("item" + (++autoId));
7069         });
7070         if(this.buttons){
7071             this.add.apply(this, this.buttons);
7072             delete this.buttons;
7073         }
7074     },
7075
7076     /**
7077      * Adds element(s) to the toolbar -- this function takes a variable number of 
7078      * arguments of mixed type and adds them to the toolbar.
7079      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7080      * <ul>
7081      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7082      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7083      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7084      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7085      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7086      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7087      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7088      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7089      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7090      * </ul>
7091      * @param {Mixed} arg2
7092      * @param {Mixed} etc.
7093      */
7094     add : function(){
7095         var a = arguments, l = a.length;
7096         for(var i = 0; i < l; i++){
7097             this._add(a[i]);
7098         }
7099     },
7100     // private..
7101     _add : function(el) {
7102         
7103         if (el.xtype) {
7104             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7105         }
7106         
7107         if (el.applyTo){ // some kind of form field
7108             return this.addField(el);
7109         } 
7110         if (el.render){ // some kind of Toolbar.Item
7111             return this.addItem(el);
7112         }
7113         if (typeof el == "string"){ // string
7114             if(el == "separator" || el == "-"){
7115                 return this.addSeparator();
7116             }
7117             if (el == " "){
7118                 return this.addSpacer();
7119             }
7120             if(el == "->"){
7121                 return this.addFill();
7122             }
7123             return this.addText(el);
7124             
7125         }
7126         if(el.tagName){ // element
7127             return this.addElement(el);
7128         }
7129         if(typeof el == "object"){ // must be button config?
7130             return this.addButton(el);
7131         }
7132         // and now what?!?!
7133         return false;
7134         
7135     },
7136     
7137     /**
7138      * Add an Xtype element
7139      * @param {Object} xtype Xtype Object
7140      * @return {Object} created Object
7141      */
7142     addxtype : function(e){
7143         return this.add(e);  
7144     },
7145     
7146     /**
7147      * Returns the Element for this toolbar.
7148      * @return {Roo.Element}
7149      */
7150     getEl : function(){
7151         return this.el;  
7152     },
7153     
7154     /**
7155      * Adds a separator
7156      * @return {Roo.Toolbar.Item} The separator item
7157      */
7158     addSeparator : function(){
7159         return this.addItem(new Roo.Toolbar.Separator());
7160     },
7161
7162     /**
7163      * Adds a spacer element
7164      * @return {Roo.Toolbar.Spacer} The spacer item
7165      */
7166     addSpacer : function(){
7167         return this.addItem(new Roo.Toolbar.Spacer());
7168     },
7169
7170     /**
7171      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7172      * @return {Roo.Toolbar.Fill} The fill item
7173      */
7174     addFill : function(){
7175         return this.addItem(new Roo.Toolbar.Fill());
7176     },
7177
7178     /**
7179      * Adds any standard HTML element to the toolbar
7180      * @param {String/HTMLElement/Element} el The element or id of the element to add
7181      * @return {Roo.Toolbar.Item} The element's item
7182      */
7183     addElement : function(el){
7184         return this.addItem(new Roo.Toolbar.Item(el));
7185     },
7186     /**
7187      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7188      * @type Roo.util.MixedCollection  
7189      */
7190     items : false,
7191      
7192     /**
7193      * Adds any Toolbar.Item or subclass
7194      * @param {Roo.Toolbar.Item} item
7195      * @return {Roo.Toolbar.Item} The item
7196      */
7197     addItem : function(item){
7198         var td = this.nextBlock();
7199         item.render(td);
7200         this.items.add(item);
7201         return item;
7202     },
7203     
7204     /**
7205      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7206      * @param {Object/Array} config A button config or array of configs
7207      * @return {Roo.Toolbar.Button/Array}
7208      */
7209     addButton : function(config){
7210         if(config instanceof Array){
7211             var buttons = [];
7212             for(var i = 0, len = config.length; i < len; i++) {
7213                 buttons.push(this.addButton(config[i]));
7214             }
7215             return buttons;
7216         }
7217         var b = config;
7218         if(!(config instanceof Roo.Toolbar.Button)){
7219             b = config.split ?
7220                 new Roo.Toolbar.SplitButton(config) :
7221                 new Roo.Toolbar.Button(config);
7222         }
7223         var td = this.nextBlock();
7224         b.render(td);
7225         this.items.add(b);
7226         return b;
7227     },
7228     
7229     /**
7230      * Adds text to the toolbar
7231      * @param {String} text The text to add
7232      * @return {Roo.Toolbar.Item} The element's item
7233      */
7234     addText : function(text){
7235         return this.addItem(new Roo.Toolbar.TextItem(text));
7236     },
7237     
7238     /**
7239      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7240      * @param {Number} index The index where the item is to be inserted
7241      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7242      * @return {Roo.Toolbar.Button/Item}
7243      */
7244     insertButton : function(index, item){
7245         if(item instanceof Array){
7246             var buttons = [];
7247             for(var i = 0, len = item.length; i < len; i++) {
7248                buttons.push(this.insertButton(index + i, item[i]));
7249             }
7250             return buttons;
7251         }
7252         if (!(item instanceof Roo.Toolbar.Button)){
7253            item = new Roo.Toolbar.Button(item);
7254         }
7255         var td = document.createElement("td");
7256         this.tr.insertBefore(td, this.tr.childNodes[index]);
7257         item.render(td);
7258         this.items.insert(index, item);
7259         return item;
7260     },
7261     
7262     /**
7263      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7264      * @param {Object} config
7265      * @return {Roo.Toolbar.Item} The element's item
7266      */
7267     addDom : function(config, returnEl){
7268         var td = this.nextBlock();
7269         Roo.DomHelper.overwrite(td, config);
7270         var ti = new Roo.Toolbar.Item(td.firstChild);
7271         ti.render(td);
7272         this.items.add(ti);
7273         return ti;
7274     },
7275
7276     /**
7277      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7278      * @type Roo.util.MixedCollection  
7279      */
7280     fields : false,
7281     
7282     /**
7283      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7284      * Note: the field should not have been rendered yet. For a field that has already been
7285      * rendered, use {@link #addElement}.
7286      * @param {Roo.form.Field} field
7287      * @return {Roo.ToolbarItem}
7288      */
7289      
7290       
7291     addField : function(field) {
7292         if (!this.fields) {
7293             var autoId = 0;
7294             this.fields = new Roo.util.MixedCollection(false, function(o){
7295                 return o.id || ("item" + (++autoId));
7296             });
7297
7298         }
7299         
7300         var td = this.nextBlock();
7301         field.render(td);
7302         var ti = new Roo.Toolbar.Item(td.firstChild);
7303         ti.render(td);
7304         this.items.add(ti);
7305         this.fields.add(field);
7306         return ti;
7307     },
7308     /**
7309      * Hide the toolbar
7310      * @method hide
7311      */
7312      
7313       
7314     hide : function()
7315     {
7316         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7317         this.el.child('div').hide();
7318     },
7319     /**
7320      * Show the toolbar
7321      * @method show
7322      */
7323     show : function()
7324     {
7325         this.el.child('div').show();
7326     },
7327       
7328     // private
7329     nextBlock : function(){
7330         var td = document.createElement("td");
7331         this.tr.appendChild(td);
7332         return td;
7333     },
7334
7335     // private
7336     destroy : function(){
7337         if(this.items){ // rendered?
7338             Roo.destroy.apply(Roo, this.items.items);
7339         }
7340         if(this.fields){ // rendered?
7341             Roo.destroy.apply(Roo, this.fields.items);
7342         }
7343         Roo.Element.uncache(this.el, this.tr);
7344     }
7345 };
7346
7347 /**
7348  * @class Roo.Toolbar.Item
7349  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7350  * @constructor
7351  * Creates a new Item
7352  * @param {HTMLElement} el 
7353  */
7354 Roo.Toolbar.Item = function(el){
7355     var cfg = {};
7356     if (typeof (el.xtype) != 'undefined') {
7357         cfg = el;
7358         el = cfg.el;
7359     }
7360     
7361     this.el = Roo.getDom(el);
7362     this.id = Roo.id(this.el);
7363     this.hidden = false;
7364     
7365     this.addEvents({
7366          /**
7367              * @event render
7368              * Fires when the button is rendered
7369              * @param {Button} this
7370              */
7371         'render': true
7372     });
7373     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7374 };
7375 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7376 //Roo.Toolbar.Item.prototype = {
7377     
7378     /**
7379      * Get this item's HTML Element
7380      * @return {HTMLElement}
7381      */
7382     getEl : function(){
7383        return this.el;  
7384     },
7385
7386     // private
7387     render : function(td){
7388         
7389          this.td = td;
7390         td.appendChild(this.el);
7391         
7392         this.fireEvent('render', this);
7393     },
7394     
7395     /**
7396      * Removes and destroys this item.
7397      */
7398     destroy : function(){
7399         this.td.parentNode.removeChild(this.td);
7400     },
7401     
7402     /**
7403      * Shows this item.
7404      */
7405     show: function(){
7406         this.hidden = false;
7407         this.td.style.display = "";
7408     },
7409     
7410     /**
7411      * Hides this item.
7412      */
7413     hide: function(){
7414         this.hidden = true;
7415         this.td.style.display = "none";
7416     },
7417     
7418     /**
7419      * Convenience function for boolean show/hide.
7420      * @param {Boolean} visible true to show/false to hide
7421      */
7422     setVisible: function(visible){
7423         if(visible) {
7424             this.show();
7425         }else{
7426             this.hide();
7427         }
7428     },
7429     
7430     /**
7431      * Try to focus this item.
7432      */
7433     focus : function(){
7434         Roo.fly(this.el).focus();
7435     },
7436     
7437     /**
7438      * Disables this item.
7439      */
7440     disable : function(){
7441         Roo.fly(this.td).addClass("x-item-disabled");
7442         this.disabled = true;
7443         this.el.disabled = true;
7444     },
7445     
7446     /**
7447      * Enables this item.
7448      */
7449     enable : function(){
7450         Roo.fly(this.td).removeClass("x-item-disabled");
7451         this.disabled = false;
7452         this.el.disabled = false;
7453     }
7454 });
7455
7456
7457 /**
7458  * @class Roo.Toolbar.Separator
7459  * @extends Roo.Toolbar.Item
7460  * A simple toolbar separator class
7461  * @constructor
7462  * Creates a new Separator
7463  */
7464 Roo.Toolbar.Separator = function(cfg){
7465     
7466     var s = document.createElement("span");
7467     s.className = "ytb-sep";
7468     if (cfg) {
7469         cfg.el = s;
7470     }
7471     
7472     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7473 };
7474 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7475     enable:Roo.emptyFn,
7476     disable:Roo.emptyFn,
7477     focus:Roo.emptyFn
7478 });
7479
7480 /**
7481  * @class Roo.Toolbar.Spacer
7482  * @extends Roo.Toolbar.Item
7483  * A simple element that adds extra horizontal space to a toolbar.
7484  * @constructor
7485  * Creates a new Spacer
7486  */
7487 Roo.Toolbar.Spacer = function(cfg){
7488     var s = document.createElement("div");
7489     s.className = "ytb-spacer";
7490     if (cfg) {
7491         cfg.el = s;
7492     }
7493     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7494 };
7495 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7496     enable:Roo.emptyFn,
7497     disable:Roo.emptyFn,
7498     focus:Roo.emptyFn
7499 });
7500
7501 /**
7502  * @class Roo.Toolbar.Fill
7503  * @extends Roo.Toolbar.Spacer
7504  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7505  * @constructor
7506  * Creates a new Spacer
7507  */
7508 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7509     // private
7510     render : function(td){
7511         td.style.width = '100%';
7512         Roo.Toolbar.Fill.superclass.render.call(this, td);
7513     }
7514 });
7515
7516 /**
7517  * @class Roo.Toolbar.TextItem
7518  * @extends Roo.Toolbar.Item
7519  * A simple class that renders text directly into a toolbar.
7520  * @constructor
7521  * Creates a new TextItem
7522  * @cfg {string} text 
7523  */
7524 Roo.Toolbar.TextItem = function(cfg){
7525     var  text = cfg || "";
7526     if (typeof(cfg) == 'object') {
7527         text = cfg.text || "";
7528     }  else {
7529         cfg = null;
7530     }
7531     var s = document.createElement("span");
7532     s.className = "ytb-text";
7533     s.innerHTML = text;
7534     if (cfg) {
7535         cfg.el  = s;
7536     }
7537     
7538     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7539 };
7540 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7541     
7542      
7543     enable:Roo.emptyFn,
7544     disable:Roo.emptyFn,
7545     focus:Roo.emptyFn
7546 });
7547
7548 /**
7549  * @class Roo.Toolbar.Button
7550  * @extends Roo.Button
7551  * A button that renders into a toolbar.
7552  * @constructor
7553  * Creates a new Button
7554  * @param {Object} config A standard {@link Roo.Button} config object
7555  */
7556 Roo.Toolbar.Button = function(config){
7557     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7558 };
7559 Roo.extend(Roo.Toolbar.Button, Roo.Button,
7560 {
7561     
7562     
7563     render : function(td){
7564         this.td = td;
7565         Roo.Toolbar.Button.superclass.render.call(this, td);
7566     },
7567     
7568     /**
7569      * Removes and destroys this button
7570      */
7571     destroy : function(){
7572         Roo.Toolbar.Button.superclass.destroy.call(this);
7573         this.td.parentNode.removeChild(this.td);
7574     },
7575     
7576     /**
7577      * Shows this button
7578      */
7579     show: function(){
7580         this.hidden = false;
7581         this.td.style.display = "";
7582     },
7583     
7584     /**
7585      * Hides this button
7586      */
7587     hide: function(){
7588         this.hidden = true;
7589         this.td.style.display = "none";
7590     },
7591
7592     /**
7593      * Disables this item
7594      */
7595     disable : function(){
7596         Roo.fly(this.td).addClass("x-item-disabled");
7597         this.disabled = true;
7598     },
7599
7600     /**
7601      * Enables this item
7602      */
7603     enable : function(){
7604         Roo.fly(this.td).removeClass("x-item-disabled");
7605         this.disabled = false;
7606     }
7607 });
7608 // backwards compat
7609 Roo.ToolbarButton = Roo.Toolbar.Button;
7610
7611 /**
7612  * @class Roo.Toolbar.SplitButton
7613  * @extends Roo.SplitButton
7614  * A menu button that renders into a toolbar.
7615  * @constructor
7616  * Creates a new SplitButton
7617  * @param {Object} config A standard {@link Roo.SplitButton} config object
7618  */
7619 Roo.Toolbar.SplitButton = function(config){
7620     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7621 };
7622 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7623     render : function(td){
7624         this.td = td;
7625         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7626     },
7627     
7628     /**
7629      * Removes and destroys this button
7630      */
7631     destroy : function(){
7632         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7633         this.td.parentNode.removeChild(this.td);
7634     },
7635     
7636     /**
7637      * Shows this button
7638      */
7639     show: function(){
7640         this.hidden = false;
7641         this.td.style.display = "";
7642     },
7643     
7644     /**
7645      * Hides this button
7646      */
7647     hide: function(){
7648         this.hidden = true;
7649         this.td.style.display = "none";
7650     }
7651 });
7652
7653 // backwards compat
7654 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7655  * Based on:
7656  * Ext JS Library 1.1.1
7657  * Copyright(c) 2006-2007, Ext JS, LLC.
7658  *
7659  * Originally Released Under LGPL - original licence link has changed is not relivant.
7660  *
7661  * Fork - LGPL
7662  * <script type="text/javascript">
7663  */
7664  
7665 /**
7666  * @class Roo.PagingToolbar
7667  * @extends Roo.Toolbar
7668  * @children   Roo.Toolbar.Item Roo.form.Field
7669  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7670  * @constructor
7671  * Create a new PagingToolbar
7672  * @param {Object} config The config object
7673  */
7674 Roo.PagingToolbar = function(el, ds, config)
7675 {
7676     // old args format still supported... - xtype is prefered..
7677     if (typeof(el) == 'object' && el.xtype) {
7678         // created from xtype...
7679         config = el;
7680         ds = el.dataSource;
7681         el = config.container;
7682     }
7683     var items = [];
7684     if (config.items) {
7685         items = config.items;
7686         config.items = [];
7687     }
7688     
7689     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7690     this.ds = ds;
7691     this.cursor = 0;
7692     this.renderButtons(this.el);
7693     this.bind(ds);
7694     
7695     // supprot items array.
7696    
7697     Roo.each(items, function(e) {
7698         this.add(Roo.factory(e));
7699     },this);
7700     
7701 };
7702
7703 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7704    
7705     /**
7706      * @cfg {String/HTMLElement/Element} container
7707      * container The id or element that will contain the toolbar
7708      */
7709     /**
7710      * @cfg {Boolean} displayInfo
7711      * True to display the displayMsg (defaults to false)
7712      */
7713     
7714     
7715     /**
7716      * @cfg {Number} pageSize
7717      * The number of records to display per page (defaults to 20)
7718      */
7719     pageSize: 20,
7720     /**
7721      * @cfg {String} displayMsg
7722      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7723      */
7724     displayMsg : 'Displaying {0} - {1} of {2}',
7725     /**
7726      * @cfg {String} emptyMsg
7727      * The message to display when no records are found (defaults to "No data to display")
7728      */
7729     emptyMsg : 'No data to display',
7730     /**
7731      * Customizable piece of the default paging text (defaults to "Page")
7732      * @type String
7733      */
7734     beforePageText : "Page",
7735     /**
7736      * Customizable piece of the default paging text (defaults to "of %0")
7737      * @type String
7738      */
7739     afterPageText : "of {0}",
7740     /**
7741      * Customizable piece of the default paging text (defaults to "First Page")
7742      * @type String
7743      */
7744     firstText : "First Page",
7745     /**
7746      * Customizable piece of the default paging text (defaults to "Previous Page")
7747      * @type String
7748      */
7749     prevText : "Previous Page",
7750     /**
7751      * Customizable piece of the default paging text (defaults to "Next Page")
7752      * @type String
7753      */
7754     nextText : "Next Page",
7755     /**
7756      * Customizable piece of the default paging text (defaults to "Last Page")
7757      * @type String
7758      */
7759     lastText : "Last Page",
7760     /**
7761      * Customizable piece of the default paging text (defaults to "Refresh")
7762      * @type String
7763      */
7764     refreshText : "Refresh",
7765
7766     // private
7767     renderButtons : function(el){
7768         Roo.PagingToolbar.superclass.render.call(this, el);
7769         this.first = this.addButton({
7770             tooltip: this.firstText,
7771             cls: "x-btn-icon x-grid-page-first",
7772             disabled: true,
7773             handler: this.onClick.createDelegate(this, ["first"])
7774         });
7775         this.prev = this.addButton({
7776             tooltip: this.prevText,
7777             cls: "x-btn-icon x-grid-page-prev",
7778             disabled: true,
7779             handler: this.onClick.createDelegate(this, ["prev"])
7780         });
7781         //this.addSeparator();
7782         this.add(this.beforePageText);
7783         this.field = Roo.get(this.addDom({
7784            tag: "input",
7785            type: "text",
7786            size: "3",
7787            value: "1",
7788            cls: "x-grid-page-number"
7789         }).el);
7790         this.field.on("keydown", this.onPagingKeydown, this);
7791         this.field.on("focus", function(){this.dom.select();});
7792         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7793         this.field.setHeight(18);
7794         //this.addSeparator();
7795         this.next = this.addButton({
7796             tooltip: this.nextText,
7797             cls: "x-btn-icon x-grid-page-next",
7798             disabled: true,
7799             handler: this.onClick.createDelegate(this, ["next"])
7800         });
7801         this.last = this.addButton({
7802             tooltip: this.lastText,
7803             cls: "x-btn-icon x-grid-page-last",
7804             disabled: true,
7805             handler: this.onClick.createDelegate(this, ["last"])
7806         });
7807         //this.addSeparator();
7808         this.loading = this.addButton({
7809             tooltip: this.refreshText,
7810             cls: "x-btn-icon x-grid-loading",
7811             handler: this.onClick.createDelegate(this, ["refresh"])
7812         });
7813
7814         if(this.displayInfo){
7815             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7816         }
7817     },
7818
7819     // private
7820     updateInfo : function(){
7821         if(this.displayEl){
7822             var count = this.ds.getCount();
7823             var msg = count == 0 ?
7824                 this.emptyMsg :
7825                 String.format(
7826                     this.displayMsg,
7827                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
7828                 );
7829             this.displayEl.update(msg);
7830         }
7831     },
7832
7833     // private
7834     onLoad : function(ds, r, o){
7835        this.cursor = o.params ? o.params.start : 0;
7836        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7837
7838        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7839        this.field.dom.value = ap;
7840        this.first.setDisabled(ap == 1);
7841        this.prev.setDisabled(ap == 1);
7842        this.next.setDisabled(ap == ps);
7843        this.last.setDisabled(ap == ps);
7844        this.loading.enable();
7845        this.updateInfo();
7846     },
7847
7848     // private
7849     getPageData : function(){
7850         var total = this.ds.getTotalCount();
7851         return {
7852             total : total,
7853             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7854             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7855         };
7856     },
7857
7858     // private
7859     onLoadError : function(){
7860         this.loading.enable();
7861     },
7862
7863     // private
7864     onPagingKeydown : function(e){
7865         var k = e.getKey();
7866         var d = this.getPageData();
7867         if(k == e.RETURN){
7868             var v = this.field.dom.value, pageNum;
7869             if(!v || isNaN(pageNum = parseInt(v, 10))){
7870                 this.field.dom.value = d.activePage;
7871                 return;
7872             }
7873             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7874             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7875             e.stopEvent();
7876         }
7877         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))
7878         {
7879           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7880           this.field.dom.value = pageNum;
7881           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7882           e.stopEvent();
7883         }
7884         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7885         {
7886           var v = this.field.dom.value, pageNum; 
7887           var increment = (e.shiftKey) ? 10 : 1;
7888           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7889             increment *= -1;
7890           }
7891           if(!v || isNaN(pageNum = parseInt(v, 10))) {
7892             this.field.dom.value = d.activePage;
7893             return;
7894           }
7895           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7896           {
7897             this.field.dom.value = parseInt(v, 10) + increment;
7898             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7899             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7900           }
7901           e.stopEvent();
7902         }
7903     },
7904
7905     // private
7906     beforeLoad : function(){
7907         if(this.loading){
7908             this.loading.disable();
7909         }
7910     },
7911
7912     // private
7913     onClick : function(which){
7914         var ds = this.ds;
7915         switch(which){
7916             case "first":
7917                 ds.load({params:{start: 0, limit: this.pageSize}});
7918             break;
7919             case "prev":
7920                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7921             break;
7922             case "next":
7923                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7924             break;
7925             case "last":
7926                 var total = ds.getTotalCount();
7927                 var extra = total % this.pageSize;
7928                 var lastStart = extra ? (total - extra) : total-this.pageSize;
7929                 ds.load({params:{start: lastStart, limit: this.pageSize}});
7930             break;
7931             case "refresh":
7932                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7933             break;
7934         }
7935     },
7936
7937     /**
7938      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7939      * @param {Roo.data.Store} store The data store to unbind
7940      */
7941     unbind : function(ds){
7942         ds.un("beforeload", this.beforeLoad, this);
7943         ds.un("load", this.onLoad, this);
7944         ds.un("loadexception", this.onLoadError, this);
7945         ds.un("remove", this.updateInfo, this);
7946         ds.un("add", this.updateInfo, this);
7947         this.ds = undefined;
7948     },
7949
7950     /**
7951      * Binds the paging toolbar to the specified {@link Roo.data.Store}
7952      * @param {Roo.data.Store} store The data store to bind
7953      */
7954     bind : function(ds){
7955         ds.on("beforeload", this.beforeLoad, this);
7956         ds.on("load", this.onLoad, this);
7957         ds.on("loadexception", this.onLoadError, this);
7958         ds.on("remove", this.updateInfo, this);
7959         ds.on("add", this.updateInfo, this);
7960         this.ds = ds;
7961     }
7962 });/*
7963  * Based on:
7964  * Ext JS Library 1.1.1
7965  * Copyright(c) 2006-2007, Ext JS, LLC.
7966  *
7967  * Originally Released Under LGPL - original licence link has changed is not relivant.
7968  *
7969  * Fork - LGPL
7970  * <script type="text/javascript">
7971  */
7972
7973 /**
7974  * @class Roo.Resizable
7975  * @extends Roo.util.Observable
7976  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
7977  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
7978  * 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
7979  * the element will be wrapped for you automatically.</p>
7980  * <p>Here is the list of valid resize handles:</p>
7981  * <pre>
7982 Value   Description
7983 ------  -------------------
7984  'n'     north
7985  's'     south
7986  'e'     east
7987  'w'     west
7988  'nw'    northwest
7989  'sw'    southwest
7990  'se'    southeast
7991  'ne'    northeast
7992  'hd'    horizontal drag
7993  'all'   all
7994 </pre>
7995  * <p>Here's an example showing the creation of a typical Resizable:</p>
7996  * <pre><code>
7997 var resizer = new Roo.Resizable("element-id", {
7998     handles: 'all',
7999     minWidth: 200,
8000     minHeight: 100,
8001     maxWidth: 500,
8002     maxHeight: 400,
8003     pinned: true
8004 });
8005 resizer.on("resize", myHandler);
8006 </code></pre>
8007  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8008  * resizer.east.setDisplayed(false);</p>
8009  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8010  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8011  * resize operation's new size (defaults to [0, 0])
8012  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8013  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8014  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8015  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8016  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8017  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8018  * @cfg {Number} width The width of the element in pixels (defaults to null)
8019  * @cfg {Number} height The height of the element in pixels (defaults to null)
8020  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8021  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8022  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8023  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8024  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8025  * in favor of the handles config option (defaults to false)
8026  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8027  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8028  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8029  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8030  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8031  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8032  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8033  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8034  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8035  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8036  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8037  * @constructor
8038  * Create a new resizable component
8039  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8040  * @param {Object} config configuration options
8041   */
8042 Roo.Resizable = function(el, config)
8043 {
8044     this.el = Roo.get(el);
8045
8046     if(config && config.wrap){
8047         config.resizeChild = this.el;
8048         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8049         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8050         this.el.setStyle("overflow", "hidden");
8051         this.el.setPositioning(config.resizeChild.getPositioning());
8052         config.resizeChild.clearPositioning();
8053         if(!config.width || !config.height){
8054             var csize = config.resizeChild.getSize();
8055             this.el.setSize(csize.width, csize.height);
8056         }
8057         if(config.pinned && !config.adjustments){
8058             config.adjustments = "auto";
8059         }
8060     }
8061
8062     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8063     this.proxy.unselectable();
8064     this.proxy.enableDisplayMode('block');
8065
8066     Roo.apply(this, config);
8067
8068     if(this.pinned){
8069         this.disableTrackOver = true;
8070         this.el.addClass("x-resizable-pinned");
8071     }
8072     // if the element isn't positioned, make it relative
8073     var position = this.el.getStyle("position");
8074     if(position != "absolute" && position != "fixed"){
8075         this.el.setStyle("position", "relative");
8076     }
8077     if(!this.handles){ // no handles passed, must be legacy style
8078         this.handles = 's,e,se';
8079         if(this.multiDirectional){
8080             this.handles += ',n,w';
8081         }
8082     }
8083     if(this.handles == "all"){
8084         this.handles = "n s e w ne nw se sw";
8085     }
8086     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8087     var ps = Roo.Resizable.positions;
8088     for(var i = 0, len = hs.length; i < len; i++){
8089         if(hs[i] && ps[hs[i]]){
8090             var pos = ps[hs[i]];
8091             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8092         }
8093     }
8094     // legacy
8095     this.corner = this.southeast;
8096     
8097     // updateBox = the box can move..
8098     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8099         this.updateBox = true;
8100     }
8101
8102     this.activeHandle = null;
8103
8104     if(this.resizeChild){
8105         if(typeof this.resizeChild == "boolean"){
8106             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8107         }else{
8108             this.resizeChild = Roo.get(this.resizeChild, true);
8109         }
8110     }
8111     
8112     if(this.adjustments == "auto"){
8113         var rc = this.resizeChild;
8114         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8115         if(rc && (hw || hn)){
8116             rc.position("relative");
8117             rc.setLeft(hw ? hw.el.getWidth() : 0);
8118             rc.setTop(hn ? hn.el.getHeight() : 0);
8119         }
8120         this.adjustments = [
8121             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8122             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8123         ];
8124     }
8125
8126     if(this.draggable){
8127         this.dd = this.dynamic ?
8128             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8129         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8130     }
8131
8132     // public events
8133     this.addEvents({
8134         /**
8135          * @event beforeresize
8136          * Fired before resize is allowed. Set enabled to false to cancel resize.
8137          * @param {Roo.Resizable} this
8138          * @param {Roo.EventObject} e The mousedown event
8139          */
8140         "beforeresize" : true,
8141         /**
8142          * @event resizing
8143          * Fired a resizing.
8144          * @param {Roo.Resizable} this
8145          * @param {Number} x The new x position
8146          * @param {Number} y The new y position
8147          * @param {Number} w The new w width
8148          * @param {Number} h The new h hight
8149          * @param {Roo.EventObject} e The mouseup event
8150          */
8151         "resizing" : true,
8152         /**
8153          * @event resize
8154          * Fired after a resize.
8155          * @param {Roo.Resizable} this
8156          * @param {Number} width The new width
8157          * @param {Number} height The new height
8158          * @param {Roo.EventObject} e The mouseup event
8159          */
8160         "resize" : true
8161     });
8162
8163     if(this.width !== null && this.height !== null){
8164         this.resizeTo(this.width, this.height);
8165     }else{
8166         this.updateChildSize();
8167     }
8168     if(Roo.isIE){
8169         this.el.dom.style.zoom = 1;
8170     }
8171     Roo.Resizable.superclass.constructor.call(this);
8172 };
8173
8174 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8175         resizeChild : false,
8176         adjustments : [0, 0],
8177         minWidth : 5,
8178         minHeight : 5,
8179         maxWidth : 10000,
8180         maxHeight : 10000,
8181         enabled : true,
8182         animate : false,
8183         duration : .35,
8184         dynamic : false,
8185         handles : false,
8186         multiDirectional : false,
8187         disableTrackOver : false,
8188         easing : 'easeOutStrong',
8189         widthIncrement : 0,
8190         heightIncrement : 0,
8191         pinned : false,
8192         width : null,
8193         height : null,
8194         preserveRatio : false,
8195         transparent: false,
8196         minX: 0,
8197         minY: 0,
8198         draggable: false,
8199
8200         /**
8201          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8202          */
8203         constrainTo: undefined,
8204         /**
8205          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8206          */
8207         resizeRegion: undefined,
8208
8209
8210     /**
8211      * Perform a manual resize
8212      * @param {Number} width
8213      * @param {Number} height
8214      */
8215     resizeTo : function(width, height){
8216         this.el.setSize(width, height);
8217         this.updateChildSize();
8218         this.fireEvent("resize", this, width, height, null);
8219     },
8220
8221     // private
8222     startSizing : function(e, handle){
8223         this.fireEvent("beforeresize", this, e);
8224         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8225
8226             if(!this.overlay){
8227                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8228                 this.overlay.unselectable();
8229                 this.overlay.enableDisplayMode("block");
8230                 this.overlay.on("mousemove", this.onMouseMove, this);
8231                 this.overlay.on("mouseup", this.onMouseUp, this);
8232             }
8233             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8234
8235             this.resizing = true;
8236             this.startBox = this.el.getBox();
8237             this.startPoint = e.getXY();
8238             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8239                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8240
8241             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8242             this.overlay.show();
8243
8244             if(this.constrainTo) {
8245                 var ct = Roo.get(this.constrainTo);
8246                 this.resizeRegion = ct.getRegion().adjust(
8247                     ct.getFrameWidth('t'),
8248                     ct.getFrameWidth('l'),
8249                     -ct.getFrameWidth('b'),
8250                     -ct.getFrameWidth('r')
8251                 );
8252             }
8253
8254             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8255             this.proxy.show();
8256             this.proxy.setBox(this.startBox);
8257             if(!this.dynamic){
8258                 this.proxy.setStyle('visibility', 'visible');
8259             }
8260         }
8261     },
8262
8263     // private
8264     onMouseDown : function(handle, e){
8265         if(this.enabled){
8266             e.stopEvent();
8267             this.activeHandle = handle;
8268             this.startSizing(e, handle);
8269         }
8270     },
8271
8272     // private
8273     onMouseUp : function(e){
8274         var size = this.resizeElement();
8275         this.resizing = false;
8276         this.handleOut();
8277         this.overlay.hide();
8278         this.proxy.hide();
8279         this.fireEvent("resize", this, size.width, size.height, e);
8280     },
8281
8282     // private
8283     updateChildSize : function(){
8284         
8285         if(this.resizeChild){
8286             var el = this.el;
8287             var child = this.resizeChild;
8288             var adj = this.adjustments;
8289             if(el.dom.offsetWidth){
8290                 var b = el.getSize(true);
8291                 child.setSize(b.width+adj[0], b.height+adj[1]);
8292             }
8293             // Second call here for IE
8294             // The first call enables instant resizing and
8295             // the second call corrects scroll bars if they
8296             // exist
8297             if(Roo.isIE){
8298                 setTimeout(function(){
8299                     if(el.dom.offsetWidth){
8300                         var b = el.getSize(true);
8301                         child.setSize(b.width+adj[0], b.height+adj[1]);
8302                     }
8303                 }, 10);
8304             }
8305         }
8306     },
8307
8308     // private
8309     snap : function(value, inc, min){
8310         if(!inc || !value) {
8311             return value;
8312         }
8313         var newValue = value;
8314         var m = value % inc;
8315         if(m > 0){
8316             if(m > (inc/2)){
8317                 newValue = value + (inc-m);
8318             }else{
8319                 newValue = value - m;
8320             }
8321         }
8322         return Math.max(min, newValue);
8323     },
8324
8325     // private
8326     resizeElement : function(){
8327         var box = this.proxy.getBox();
8328         if(this.updateBox){
8329             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8330         }else{
8331             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8332         }
8333         this.updateChildSize();
8334         if(!this.dynamic){
8335             this.proxy.hide();
8336         }
8337         return box;
8338     },
8339
8340     // private
8341     constrain : function(v, diff, m, mx){
8342         if(v - diff < m){
8343             diff = v - m;
8344         }else if(v - diff > mx){
8345             diff = mx - v;
8346         }
8347         return diff;
8348     },
8349
8350     // private
8351     onMouseMove : function(e){
8352         
8353         if(this.enabled){
8354             try{// try catch so if something goes wrong the user doesn't get hung
8355
8356             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8357                 return;
8358             }
8359
8360             //var curXY = this.startPoint;
8361             var curSize = this.curSize || this.startBox;
8362             var x = this.startBox.x, y = this.startBox.y;
8363             var ox = x, oy = y;
8364             var w = curSize.width, h = curSize.height;
8365             var ow = w, oh = h;
8366             var mw = this.minWidth, mh = this.minHeight;
8367             var mxw = this.maxWidth, mxh = this.maxHeight;
8368             var wi = this.widthIncrement;
8369             var hi = this.heightIncrement;
8370
8371             var eventXY = e.getXY();
8372             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8373             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8374
8375             var pos = this.activeHandle.position;
8376
8377             switch(pos){
8378                 case "east":
8379                     w += diffX;
8380                     w = Math.min(Math.max(mw, w), mxw);
8381                     break;
8382              
8383                 case "south":
8384                     h += diffY;
8385                     h = Math.min(Math.max(mh, h), mxh);
8386                     break;
8387                 case "southeast":
8388                     w += diffX;
8389                     h += diffY;
8390                     w = Math.min(Math.max(mw, w), mxw);
8391                     h = Math.min(Math.max(mh, h), mxh);
8392                     break;
8393                 case "north":
8394                     diffY = this.constrain(h, diffY, mh, mxh);
8395                     y += diffY;
8396                     h -= diffY;
8397                     break;
8398                 case "hdrag":
8399                     
8400                     if (wi) {
8401                         var adiffX = Math.abs(diffX);
8402                         var sub = (adiffX % wi); // how much 
8403                         if (sub > (wi/2)) { // far enough to snap
8404                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8405                         } else {
8406                             // remove difference.. 
8407                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8408                         }
8409                     }
8410                     x += diffX;
8411                     x = Math.max(this.minX, x);
8412                     break;
8413                 case "west":
8414                     diffX = this.constrain(w, diffX, mw, mxw);
8415                     x += diffX;
8416                     w -= diffX;
8417                     break;
8418                 case "northeast":
8419                     w += diffX;
8420                     w = Math.min(Math.max(mw, w), mxw);
8421                     diffY = this.constrain(h, diffY, mh, mxh);
8422                     y += diffY;
8423                     h -= diffY;
8424                     break;
8425                 case "northwest":
8426                     diffX = this.constrain(w, diffX, mw, mxw);
8427                     diffY = this.constrain(h, diffY, mh, mxh);
8428                     y += diffY;
8429                     h -= diffY;
8430                     x += diffX;
8431                     w -= diffX;
8432                     break;
8433                case "southwest":
8434                     diffX = this.constrain(w, diffX, mw, mxw);
8435                     h += diffY;
8436                     h = Math.min(Math.max(mh, h), mxh);
8437                     x += diffX;
8438                     w -= diffX;
8439                     break;
8440             }
8441
8442             var sw = this.snap(w, wi, mw);
8443             var sh = this.snap(h, hi, mh);
8444             if(sw != w || sh != h){
8445                 switch(pos){
8446                     case "northeast":
8447                         y -= sh - h;
8448                     break;
8449                     case "north":
8450                         y -= sh - h;
8451                         break;
8452                     case "southwest":
8453                         x -= sw - w;
8454                     break;
8455                     case "west":
8456                         x -= sw - w;
8457                         break;
8458                     case "northwest":
8459                         x -= sw - w;
8460                         y -= sh - h;
8461                     break;
8462                 }
8463                 w = sw;
8464                 h = sh;
8465             }
8466
8467             if(this.preserveRatio){
8468                 switch(pos){
8469                     case "southeast":
8470                     case "east":
8471                         h = oh * (w/ow);
8472                         h = Math.min(Math.max(mh, h), mxh);
8473                         w = ow * (h/oh);
8474                        break;
8475                     case "south":
8476                         w = ow * (h/oh);
8477                         w = Math.min(Math.max(mw, w), mxw);
8478                         h = oh * (w/ow);
8479                         break;
8480                     case "northeast":
8481                         w = ow * (h/oh);
8482                         w = Math.min(Math.max(mw, w), mxw);
8483                         h = oh * (w/ow);
8484                     break;
8485                     case "north":
8486                         var tw = w;
8487                         w = ow * (h/oh);
8488                         w = Math.min(Math.max(mw, w), mxw);
8489                         h = oh * (w/ow);
8490                         x += (tw - w) / 2;
8491                         break;
8492                     case "southwest":
8493                         h = oh * (w/ow);
8494                         h = Math.min(Math.max(mh, h), mxh);
8495                         var tw = w;
8496                         w = ow * (h/oh);
8497                         x += tw - w;
8498                         break;
8499                     case "west":
8500                         var th = h;
8501                         h = oh * (w/ow);
8502                         h = Math.min(Math.max(mh, h), mxh);
8503                         y += (th - h) / 2;
8504                         var tw = w;
8505                         w = ow * (h/oh);
8506                         x += tw - w;
8507                        break;
8508                     case "northwest":
8509                         var tw = w;
8510                         var th = h;
8511                         h = oh * (w/ow);
8512                         h = Math.min(Math.max(mh, h), mxh);
8513                         w = ow * (h/oh);
8514                         y += th - h;
8515                         x += tw - w;
8516                        break;
8517
8518                 }
8519             }
8520             if (pos == 'hdrag') {
8521                 w = ow;
8522             }
8523             this.proxy.setBounds(x, y, w, h);
8524             if(this.dynamic){
8525                 this.resizeElement();
8526             }
8527             }catch(e){}
8528         }
8529         this.fireEvent("resizing", this, x, y, w, h, e);
8530     },
8531
8532     // private
8533     handleOver : function(){
8534         if(this.enabled){
8535             this.el.addClass("x-resizable-over");
8536         }
8537     },
8538
8539     // private
8540     handleOut : function(){
8541         if(!this.resizing){
8542             this.el.removeClass("x-resizable-over");
8543         }
8544     },
8545
8546     /**
8547      * Returns the element this component is bound to.
8548      * @return {Roo.Element}
8549      */
8550     getEl : function(){
8551         return this.el;
8552     },
8553
8554     /**
8555      * Returns the resizeChild element (or null).
8556      * @return {Roo.Element}
8557      */
8558     getResizeChild : function(){
8559         return this.resizeChild;
8560     },
8561     groupHandler : function()
8562     {
8563         
8564     },
8565     /**
8566      * Destroys this resizable. If the element was wrapped and
8567      * removeEl is not true then the element remains.
8568      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8569      */
8570     destroy : function(removeEl){
8571         this.proxy.remove();
8572         if(this.overlay){
8573             this.overlay.removeAllListeners();
8574             this.overlay.remove();
8575         }
8576         var ps = Roo.Resizable.positions;
8577         for(var k in ps){
8578             if(typeof ps[k] != "function" && this[ps[k]]){
8579                 var h = this[ps[k]];
8580                 h.el.removeAllListeners();
8581                 h.el.remove();
8582             }
8583         }
8584         if(removeEl){
8585             this.el.update("");
8586             this.el.remove();
8587         }
8588     }
8589 });
8590
8591 // private
8592 // hash to map config positions to true positions
8593 Roo.Resizable.positions = {
8594     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8595     hd: "hdrag"
8596 };
8597
8598 // private
8599 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8600     if(!this.tpl){
8601         // only initialize the template if resizable is used
8602         var tpl = Roo.DomHelper.createTemplate(
8603             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8604         );
8605         tpl.compile();
8606         Roo.Resizable.Handle.prototype.tpl = tpl;
8607     }
8608     this.position = pos;
8609     this.rz = rz;
8610     // show north drag fro topdra
8611     var handlepos = pos == 'hdrag' ? 'north' : pos;
8612     
8613     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8614     if (pos == 'hdrag') {
8615         this.el.setStyle('cursor', 'pointer');
8616     }
8617     this.el.unselectable();
8618     if(transparent){
8619         this.el.setOpacity(0);
8620     }
8621     this.el.on("mousedown", this.onMouseDown, this);
8622     if(!disableTrackOver){
8623         this.el.on("mouseover", this.onMouseOver, this);
8624         this.el.on("mouseout", this.onMouseOut, this);
8625     }
8626 };
8627
8628 // private
8629 Roo.Resizable.Handle.prototype = {
8630     afterResize : function(rz){
8631         Roo.log('after?');
8632         // do nothing
8633     },
8634     // private
8635     onMouseDown : function(e){
8636         this.rz.onMouseDown(this, e);
8637     },
8638     // private
8639     onMouseOver : function(e){
8640         this.rz.handleOver(this, e);
8641     },
8642     // private
8643     onMouseOut : function(e){
8644         this.rz.handleOut(this, e);
8645     }
8646 };/*
8647  * Based on:
8648  * Ext JS Library 1.1.1
8649  * Copyright(c) 2006-2007, Ext JS, LLC.
8650  *
8651  * Originally Released Under LGPL - original licence link has changed is not relivant.
8652  *
8653  * Fork - LGPL
8654  * <script type="text/javascript">
8655  */
8656
8657 /**
8658  * @class Roo.Editor
8659  * @extends Roo.Component
8660  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8661  * @constructor
8662  * Create a new Editor
8663  * @param {Roo.form.Field} field The Field object (or descendant)
8664  * @param {Object} config The config object
8665  */
8666 Roo.Editor = function(field, config){
8667     Roo.Editor.superclass.constructor.call(this, config);
8668     this.field = field;
8669     this.addEvents({
8670         /**
8671              * @event beforestartedit
8672              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
8673              * false from the handler of this event.
8674              * @param {Editor} this
8675              * @param {Roo.Element} boundEl The underlying element bound to this editor
8676              * @param {Mixed} value The field value being set
8677              */
8678         "beforestartedit" : true,
8679         /**
8680              * @event startedit
8681              * Fires when this editor is displayed
8682              * @param {Roo.Element} boundEl The underlying element bound to this editor
8683              * @param {Mixed} value The starting field value
8684              */
8685         "startedit" : true,
8686         /**
8687              * @event beforecomplete
8688              * Fires after a change has been made to the field, but before the change is reflected in the underlying
8689              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
8690              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8691              * event will not fire since no edit actually occurred.
8692              * @param {Editor} this
8693              * @param {Mixed} value The current field value
8694              * @param {Mixed} startValue The original field value
8695              */
8696         "beforecomplete" : true,
8697         /**
8698              * @event complete
8699              * Fires after editing is complete and any changed value has been written to the underlying field.
8700              * @param {Editor} this
8701              * @param {Mixed} value The current field value
8702              * @param {Mixed} startValue The original field value
8703              */
8704         "complete" : true,
8705         /**
8706          * @event specialkey
8707          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8708          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8709          * @param {Roo.form.Field} this
8710          * @param {Roo.EventObject} e The event object
8711          */
8712         "specialkey" : true
8713     });
8714 };
8715
8716 Roo.extend(Roo.Editor, Roo.Component, {
8717     /**
8718      * @cfg {Boolean/String} autosize
8719      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8720      * or "height" to adopt the height only (defaults to false)
8721      */
8722     /**
8723      * @cfg {Boolean} revertInvalid
8724      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8725      * validation fails (defaults to true)
8726      */
8727     /**
8728      * @cfg {Boolean} ignoreNoChange
8729      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8730      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
8731      * will never be ignored.
8732      */
8733     /**
8734      * @cfg {Boolean} hideEl
8735      * False to keep the bound element visible while the editor is displayed (defaults to true)
8736      */
8737     /**
8738      * @cfg {Mixed} value
8739      * The data value of the underlying field (defaults to "")
8740      */
8741     value : "",
8742     /**
8743      * @cfg {String} alignment
8744      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8745      */
8746     alignment: "c-c?",
8747     /**
8748      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8749      * for bottom-right shadow (defaults to "frame")
8750      */
8751     shadow : "frame",
8752     /**
8753      * @cfg {Boolean} constrain True to constrain the editor to the viewport
8754      */
8755     constrain : false,
8756     /**
8757      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8758      */
8759     completeOnEnter : false,
8760     /**
8761      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8762      */
8763     cancelOnEsc : false,
8764     /**
8765      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8766      */
8767     updateEl : false,
8768
8769     // private
8770     onRender : function(ct, position){
8771         this.el = new Roo.Layer({
8772             shadow: this.shadow,
8773             cls: "x-editor",
8774             parentEl : ct,
8775             shim : this.shim,
8776             shadowOffset:4,
8777             id: this.id,
8778             constrain: this.constrain
8779         });
8780         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8781         if(this.field.msgTarget != 'title'){
8782             this.field.msgTarget = 'qtip';
8783         }
8784         this.field.render(this.el);
8785         if(Roo.isGecko){
8786             this.field.el.dom.setAttribute('autocomplete', 'off');
8787         }
8788         this.field.on("specialkey", this.onSpecialKey, this);
8789         if(this.swallowKeys){
8790             this.field.el.swallowEvent(['keydown','keypress']);
8791         }
8792         this.field.show();
8793         this.field.on("blur", this.onBlur, this);
8794         if(this.field.grow){
8795             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
8796         }
8797     },
8798
8799     onSpecialKey : function(field, e)
8800     {
8801         //Roo.log('editor onSpecialKey');
8802         if(this.completeOnEnter && e.getKey() == e.ENTER){
8803             e.stopEvent();
8804             this.completeEdit();
8805             return;
8806         }
8807         // do not fire special key otherwise it might hide close the editor...
8808         if(e.getKey() == e.ENTER){    
8809             return;
8810         }
8811         if(this.cancelOnEsc && e.getKey() == e.ESC){
8812             this.cancelEdit();
8813             return;
8814         } 
8815         this.fireEvent('specialkey', field, e);
8816     
8817     },
8818
8819     /**
8820      * Starts the editing process and shows the editor.
8821      * @param {String/HTMLElement/Element} el The element to edit
8822      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8823       * to the innerHTML of el.
8824      */
8825     startEdit : function(el, value){
8826         if(this.editing){
8827             this.completeEdit();
8828         }
8829         this.boundEl = Roo.get(el);
8830         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8831         if(!this.rendered){
8832             this.render(this.parentEl || document.body);
8833         }
8834         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8835             return;
8836         }
8837         this.startValue = v;
8838         this.field.setValue(v);
8839         if(this.autoSize){
8840             var sz = this.boundEl.getSize();
8841             switch(this.autoSize){
8842                 case "width":
8843                 this.setSize(sz.width,  "");
8844                 break;
8845                 case "height":
8846                 this.setSize("",  sz.height);
8847                 break;
8848                 default:
8849                 this.setSize(sz.width,  sz.height);
8850             }
8851         }
8852         this.el.alignTo(this.boundEl, this.alignment);
8853         this.editing = true;
8854         if(Roo.QuickTips){
8855             Roo.QuickTips.disable();
8856         }
8857         this.show();
8858     },
8859
8860     /**
8861      * Sets the height and width of this editor.
8862      * @param {Number} width The new width
8863      * @param {Number} height The new height
8864      */
8865     setSize : function(w, h){
8866         this.field.setSize(w, h);
8867         if(this.el){
8868             this.el.sync();
8869         }
8870     },
8871
8872     /**
8873      * Realigns the editor to the bound field based on the current alignment config value.
8874      */
8875     realign : function(){
8876         this.el.alignTo(this.boundEl, this.alignment);
8877     },
8878
8879     /**
8880      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8881      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8882      */
8883     completeEdit : function(remainVisible){
8884         if(!this.editing){
8885             return;
8886         }
8887         var v = this.getValue();
8888         if(this.revertInvalid !== false && !this.field.isValid()){
8889             v = this.startValue;
8890             this.cancelEdit(true);
8891         }
8892         if(String(v) === String(this.startValue) && this.ignoreNoChange){
8893             this.editing = false;
8894             this.hide();
8895             return;
8896         }
8897         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8898             this.editing = false;
8899             if(this.updateEl && this.boundEl){
8900                 this.boundEl.update(v);
8901             }
8902             if(remainVisible !== true){
8903                 this.hide();
8904             }
8905             this.fireEvent("complete", this, v, this.startValue);
8906         }
8907     },
8908
8909     // private
8910     onShow : function(){
8911         this.el.show();
8912         if(this.hideEl !== false){
8913             this.boundEl.hide();
8914         }
8915         this.field.show();
8916         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8917             this.fixIEFocus = true;
8918             this.deferredFocus.defer(50, this);
8919         }else{
8920             this.field.focus();
8921         }
8922         this.fireEvent("startedit", this.boundEl, this.startValue);
8923     },
8924
8925     deferredFocus : function(){
8926         if(this.editing){
8927             this.field.focus();
8928         }
8929     },
8930
8931     /**
8932      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
8933      * reverted to the original starting value.
8934      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8935      * cancel (defaults to false)
8936      */
8937     cancelEdit : function(remainVisible){
8938         if(this.editing){
8939             this.setValue(this.startValue);
8940             if(remainVisible !== true){
8941                 this.hide();
8942             }
8943         }
8944     },
8945
8946     // private
8947     onBlur : function(){
8948         if(this.allowBlur !== true && this.editing){
8949             this.completeEdit();
8950         }
8951     },
8952
8953     // private
8954     onHide : function(){
8955         if(this.editing){
8956             this.completeEdit();
8957             return;
8958         }
8959         this.field.blur();
8960         if(this.field.collapse){
8961             this.field.collapse();
8962         }
8963         this.el.hide();
8964         if(this.hideEl !== false){
8965             this.boundEl.show();
8966         }
8967         if(Roo.QuickTips){
8968             Roo.QuickTips.enable();
8969         }
8970     },
8971
8972     /**
8973      * Sets the data value of the editor
8974      * @param {Mixed} value Any valid value supported by the underlying field
8975      */
8976     setValue : function(v){
8977         this.field.setValue(v);
8978     },
8979
8980     /**
8981      * Gets the data value of the editor
8982      * @return {Mixed} The data value
8983      */
8984     getValue : function(){
8985         return this.field.getValue();
8986     }
8987 });/*
8988  * Based on:
8989  * Ext JS Library 1.1.1
8990  * Copyright(c) 2006-2007, Ext JS, LLC.
8991  *
8992  * Originally Released Under LGPL - original licence link has changed is not relivant.
8993  *
8994  * Fork - LGPL
8995  * <script type="text/javascript">
8996  */
8997  
8998 /**
8999  * @class Roo.BasicDialog
9000  * @extends Roo.util.Observable
9001  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9002  * <pre><code>
9003 var dlg = new Roo.BasicDialog("my-dlg", {
9004     height: 200,
9005     width: 300,
9006     minHeight: 100,
9007     minWidth: 150,
9008     modal: true,
9009     proxyDrag: true,
9010     shadow: true
9011 });
9012 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9013 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9014 dlg.addButton('Cancel', dlg.hide, dlg);
9015 dlg.show();
9016 </code></pre>
9017   <b>A Dialog should always be a direct child of the body element.</b>
9018  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9019  * @cfg {String} title Default text to display in the title bar (defaults to null)
9020  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9021  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9022  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9023  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9024  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9025  * (defaults to null with no animation)
9026  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9027  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9028  * property for valid values (defaults to 'all')
9029  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9030  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9031  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9032  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9033  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9034  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9035  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9036  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9037  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9038  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9039  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9040  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9041  * draggable = true (defaults to false)
9042  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9043  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9044  * shadow (defaults to false)
9045  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9046  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9047  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9048  * @cfg {Array} buttons Array of buttons
9049  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9050  * @constructor
9051  * Create a new BasicDialog.
9052  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9053  * @param {Object} config Configuration options
9054  */
9055 Roo.BasicDialog = function(el, config){
9056     this.el = Roo.get(el);
9057     var dh = Roo.DomHelper;
9058     if(!this.el && config && config.autoCreate){
9059         if(typeof config.autoCreate == "object"){
9060             if(!config.autoCreate.id){
9061                 config.autoCreate.id = el;
9062             }
9063             this.el = dh.append(document.body,
9064                         config.autoCreate, true);
9065         }else{
9066             this.el = dh.append(document.body,
9067                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9068         }
9069     }
9070     el = this.el;
9071     el.setDisplayed(true);
9072     el.hide = this.hideAction;
9073     this.id = el.id;
9074     el.addClass("x-dlg");
9075
9076     Roo.apply(this, config);
9077
9078     this.proxy = el.createProxy("x-dlg-proxy");
9079     this.proxy.hide = this.hideAction;
9080     this.proxy.setOpacity(.5);
9081     this.proxy.hide();
9082
9083     if(config.width){
9084         el.setWidth(config.width);
9085     }
9086     if(config.height){
9087         el.setHeight(config.height);
9088     }
9089     this.size = el.getSize();
9090     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9091         this.xy = [config.x,config.y];
9092     }else{
9093         this.xy = el.getCenterXY(true);
9094     }
9095     /** The header element @type Roo.Element */
9096     this.header = el.child("> .x-dlg-hd");
9097     /** The body element @type Roo.Element */
9098     this.body = el.child("> .x-dlg-bd");
9099     /** The footer element @type Roo.Element */
9100     this.footer = el.child("> .x-dlg-ft");
9101
9102     if(!this.header){
9103         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9104     }
9105     if(!this.body){
9106         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9107     }
9108
9109     this.header.unselectable();
9110     if(this.title){
9111         this.header.update(this.title);
9112     }
9113     // this element allows the dialog to be focused for keyboard event
9114     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9115     this.focusEl.swallowEvent("click", true);
9116
9117     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9118
9119     // wrap the body and footer for special rendering
9120     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9121     if(this.footer){
9122         this.bwrap.dom.appendChild(this.footer.dom);
9123     }
9124
9125     this.bg = this.el.createChild({
9126         tag: "div", cls:"x-dlg-bg",
9127         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9128     });
9129     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9130
9131
9132     if(this.autoScroll !== false && !this.autoTabs){
9133         this.body.setStyle("overflow", "auto");
9134     }
9135
9136     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9137
9138     if(this.closable !== false){
9139         this.el.addClass("x-dlg-closable");
9140         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9141         this.close.on("click", this.closeClick, this);
9142         this.close.addClassOnOver("x-dlg-close-over");
9143     }
9144     if(this.collapsible !== false){
9145         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9146         this.collapseBtn.on("click", this.collapseClick, this);
9147         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9148         this.header.on("dblclick", this.collapseClick, this);
9149     }
9150     if(this.resizable !== false){
9151         this.el.addClass("x-dlg-resizable");
9152         this.resizer = new Roo.Resizable(el, {
9153             minWidth: this.minWidth || 80,
9154             minHeight:this.minHeight || 80,
9155             handles: this.resizeHandles || "all",
9156             pinned: true
9157         });
9158         this.resizer.on("beforeresize", this.beforeResize, this);
9159         this.resizer.on("resize", this.onResize, this);
9160     }
9161     if(this.draggable !== false){
9162         el.addClass("x-dlg-draggable");
9163         if (!this.proxyDrag) {
9164             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9165         }
9166         else {
9167             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9168         }
9169         dd.setHandleElId(this.header.id);
9170         dd.endDrag = this.endMove.createDelegate(this);
9171         dd.startDrag = this.startMove.createDelegate(this);
9172         dd.onDrag = this.onDrag.createDelegate(this);
9173         dd.scroll = false;
9174         this.dd = dd;
9175     }
9176     if(this.modal){
9177         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9178         this.mask.enableDisplayMode("block");
9179         this.mask.hide();
9180         this.el.addClass("x-dlg-modal");
9181     }
9182     if(this.shadow){
9183         this.shadow = new Roo.Shadow({
9184             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9185             offset : this.shadowOffset
9186         });
9187     }else{
9188         this.shadowOffset = 0;
9189     }
9190     if(Roo.useShims && this.shim !== false){
9191         this.shim = this.el.createShim();
9192         this.shim.hide = this.hideAction;
9193         this.shim.hide();
9194     }else{
9195         this.shim = false;
9196     }
9197     if(this.autoTabs){
9198         this.initTabs();
9199     }
9200     if (this.buttons) { 
9201         var bts= this.buttons;
9202         this.buttons = [];
9203         Roo.each(bts, function(b) {
9204             this.addButton(b);
9205         }, this);
9206     }
9207     
9208     
9209     this.addEvents({
9210         /**
9211          * @event keydown
9212          * Fires when a key is pressed
9213          * @param {Roo.BasicDialog} this
9214          * @param {Roo.EventObject} e
9215          */
9216         "keydown" : true,
9217         /**
9218          * @event move
9219          * Fires when this dialog is moved by the user.
9220          * @param {Roo.BasicDialog} this
9221          * @param {Number} x The new page X
9222          * @param {Number} y The new page Y
9223          */
9224         "move" : true,
9225         /**
9226          * @event resize
9227          * Fires when this dialog is resized by the user.
9228          * @param {Roo.BasicDialog} this
9229          * @param {Number} width The new width
9230          * @param {Number} height The new height
9231          */
9232         "resize" : true,
9233         /**
9234          * @event beforehide
9235          * Fires before this dialog is hidden.
9236          * @param {Roo.BasicDialog} this
9237          */
9238         "beforehide" : true,
9239         /**
9240          * @event hide
9241          * Fires when this dialog is hidden.
9242          * @param {Roo.BasicDialog} this
9243          */
9244         "hide" : true,
9245         /**
9246          * @event beforeshow
9247          * Fires before this dialog is shown.
9248          * @param {Roo.BasicDialog} this
9249          */
9250         "beforeshow" : true,
9251         /**
9252          * @event show
9253          * Fires when this dialog is shown.
9254          * @param {Roo.BasicDialog} this
9255          */
9256         "show" : true
9257     });
9258     el.on("keydown", this.onKeyDown, this);
9259     el.on("mousedown", this.toFront, this);
9260     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9261     this.el.hide();
9262     Roo.DialogManager.register(this);
9263     Roo.BasicDialog.superclass.constructor.call(this);
9264 };
9265
9266 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9267     shadowOffset: Roo.isIE ? 6 : 5,
9268     minHeight: 80,
9269     minWidth: 200,
9270     minButtonWidth: 75,
9271     defaultButton: null,
9272     buttonAlign: "right",
9273     tabTag: 'div',
9274     firstShow: true,
9275
9276     /**
9277      * Sets the dialog title text
9278      * @param {String} text The title text to display
9279      * @return {Roo.BasicDialog} this
9280      */
9281     setTitle : function(text){
9282         this.header.update(text);
9283         return this;
9284     },
9285
9286     // private
9287     closeClick : function(){
9288         this.hide();
9289     },
9290
9291     // private
9292     collapseClick : function(){
9293         this[this.collapsed ? "expand" : "collapse"]();
9294     },
9295
9296     /**
9297      * Collapses the dialog to its minimized state (only the title bar is visible).
9298      * Equivalent to the user clicking the collapse dialog button.
9299      */
9300     collapse : function(){
9301         if(!this.collapsed){
9302             this.collapsed = true;
9303             this.el.addClass("x-dlg-collapsed");
9304             this.restoreHeight = this.el.getHeight();
9305             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9306         }
9307     },
9308
9309     /**
9310      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9311      * clicking the expand dialog button.
9312      */
9313     expand : function(){
9314         if(this.collapsed){
9315             this.collapsed = false;
9316             this.el.removeClass("x-dlg-collapsed");
9317             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9318         }
9319     },
9320
9321     /**
9322      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9323      * @return {Roo.TabPanel} The tabs component
9324      */
9325     initTabs : function(){
9326         var tabs = this.getTabs();
9327         while(tabs.getTab(0)){
9328             tabs.removeTab(0);
9329         }
9330         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9331             var dom = el.dom;
9332             tabs.addTab(Roo.id(dom), dom.title);
9333             dom.title = "";
9334         });
9335         tabs.activate(0);
9336         return tabs;
9337     },
9338
9339     // private
9340     beforeResize : function(){
9341         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9342     },
9343
9344     // private
9345     onResize : function(){
9346         this.refreshSize();
9347         this.syncBodyHeight();
9348         this.adjustAssets();
9349         this.focus();
9350         this.fireEvent("resize", this, this.size.width, this.size.height);
9351     },
9352
9353     // private
9354     onKeyDown : function(e){
9355         if(this.isVisible()){
9356             this.fireEvent("keydown", this, e);
9357         }
9358     },
9359
9360     /**
9361      * Resizes the dialog.
9362      * @param {Number} width
9363      * @param {Number} height
9364      * @return {Roo.BasicDialog} this
9365      */
9366     resizeTo : function(width, height){
9367         this.el.setSize(width, height);
9368         this.size = {width: width, height: height};
9369         this.syncBodyHeight();
9370         if(this.fixedcenter){
9371             this.center();
9372         }
9373         if(this.isVisible()){
9374             this.constrainXY();
9375             this.adjustAssets();
9376         }
9377         this.fireEvent("resize", this, width, height);
9378         return this;
9379     },
9380
9381
9382     /**
9383      * Resizes the dialog to fit the specified content size.
9384      * @param {Number} width
9385      * @param {Number} height
9386      * @return {Roo.BasicDialog} this
9387      */
9388     setContentSize : function(w, h){
9389         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9390         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9391         //if(!this.el.isBorderBox()){
9392             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9393             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9394         //}
9395         if(this.tabs){
9396             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9397             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9398         }
9399         this.resizeTo(w, h);
9400         return this;
9401     },
9402
9403     /**
9404      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9405      * executed in response to a particular key being pressed while the dialog is active.
9406      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9407      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9408      * @param {Function} fn The function to call
9409      * @param {Object} scope (optional) The scope of the function
9410      * @return {Roo.BasicDialog} this
9411      */
9412     addKeyListener : function(key, fn, scope){
9413         var keyCode, shift, ctrl, alt;
9414         if(typeof key == "object" && !(key instanceof Array)){
9415             keyCode = key["key"];
9416             shift = key["shift"];
9417             ctrl = key["ctrl"];
9418             alt = key["alt"];
9419         }else{
9420             keyCode = key;
9421         }
9422         var handler = function(dlg, e){
9423             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9424                 var k = e.getKey();
9425                 if(keyCode instanceof Array){
9426                     for(var i = 0, len = keyCode.length; i < len; i++){
9427                         if(keyCode[i] == k){
9428                           fn.call(scope || window, dlg, k, e);
9429                           return;
9430                         }
9431                     }
9432                 }else{
9433                     if(k == keyCode){
9434                         fn.call(scope || window, dlg, k, e);
9435                     }
9436                 }
9437             }
9438         };
9439         this.on("keydown", handler);
9440         return this;
9441     },
9442
9443     /**
9444      * Returns the TabPanel component (creates it if it doesn't exist).
9445      * Note: If you wish to simply check for the existence of tabs without creating them,
9446      * check for a null 'tabs' property.
9447      * @return {Roo.TabPanel} The tabs component
9448      */
9449     getTabs : function(){
9450         if(!this.tabs){
9451             this.el.addClass("x-dlg-auto-tabs");
9452             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9453             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9454         }
9455         return this.tabs;
9456     },
9457
9458     /**
9459      * Adds a button to the footer section of the dialog.
9460      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9461      * object or a valid Roo.DomHelper element config
9462      * @param {Function} handler The function called when the button is clicked
9463      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9464      * @return {Roo.Button} The new button
9465      */
9466     addButton : function(config, handler, scope){
9467         var dh = Roo.DomHelper;
9468         if(!this.footer){
9469             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9470         }
9471         if(!this.btnContainer){
9472             var tb = this.footer.createChild({
9473
9474                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9475                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9476             }, null, true);
9477             this.btnContainer = tb.firstChild.firstChild.firstChild;
9478         }
9479         var bconfig = {
9480             handler: handler,
9481             scope: scope,
9482             minWidth: this.minButtonWidth,
9483             hideParent:true
9484         };
9485         if(typeof config == "string"){
9486             bconfig.text = config;
9487         }else{
9488             if(config.tag){
9489                 bconfig.dhconfig = config;
9490             }else{
9491                 Roo.apply(bconfig, config);
9492             }
9493         }
9494         var fc = false;
9495         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9496             bconfig.position = Math.max(0, bconfig.position);
9497             fc = this.btnContainer.childNodes[bconfig.position];
9498         }
9499          
9500         var btn = new Roo.Button(
9501             fc ? 
9502                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9503                 : this.btnContainer.appendChild(document.createElement("td")),
9504             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9505             bconfig
9506         );
9507         this.syncBodyHeight();
9508         if(!this.buttons){
9509             /**
9510              * Array of all the buttons that have been added to this dialog via addButton
9511              * @type Array
9512              */
9513             this.buttons = [];
9514         }
9515         this.buttons.push(btn);
9516         return btn;
9517     },
9518
9519     /**
9520      * Sets the default button to be focused when the dialog is displayed.
9521      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9522      * @return {Roo.BasicDialog} this
9523      */
9524     setDefaultButton : function(btn){
9525         this.defaultButton = btn;
9526         return this;
9527     },
9528
9529     // private
9530     getHeaderFooterHeight : function(safe){
9531         var height = 0;
9532         if(this.header){
9533            height += this.header.getHeight();
9534         }
9535         if(this.footer){
9536            var fm = this.footer.getMargins();
9537             height += (this.footer.getHeight()+fm.top+fm.bottom);
9538         }
9539         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9540         height += this.centerBg.getPadding("tb");
9541         return height;
9542     },
9543
9544     // private
9545     syncBodyHeight : function()
9546     {
9547         var bd = this.body, // the text
9548             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9549             bw = this.bwrap;
9550         var height = this.size.height - this.getHeaderFooterHeight(false);
9551         bd.setHeight(height-bd.getMargins("tb"));
9552         var hh = this.header.getHeight();
9553         var h = this.size.height-hh;
9554         cb.setHeight(h);
9555         
9556         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9557         bw.setHeight(h-cb.getPadding("tb"));
9558         
9559         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9560         bd.setWidth(bw.getWidth(true));
9561         if(this.tabs){
9562             this.tabs.syncHeight();
9563             if(Roo.isIE){
9564                 this.tabs.el.repaint();
9565             }
9566         }
9567     },
9568
9569     /**
9570      * Restores the previous state of the dialog if Roo.state is configured.
9571      * @return {Roo.BasicDialog} this
9572      */
9573     restoreState : function(){
9574         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9575         if(box && box.width){
9576             this.xy = [box.x, box.y];
9577             this.resizeTo(box.width, box.height);
9578         }
9579         return this;
9580     },
9581
9582     // private
9583     beforeShow : function(){
9584         this.expand();
9585         if(this.fixedcenter){
9586             this.xy = this.el.getCenterXY(true);
9587         }
9588         if(this.modal){
9589             Roo.get(document.body).addClass("x-body-masked");
9590             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9591             this.mask.show();
9592         }
9593         this.constrainXY();
9594     },
9595
9596     // private
9597     animShow : function(){
9598         var b = Roo.get(this.animateTarget).getBox();
9599         this.proxy.setSize(b.width, b.height);
9600         this.proxy.setLocation(b.x, b.y);
9601         this.proxy.show();
9602         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9603                     true, .35, this.showEl.createDelegate(this));
9604     },
9605
9606     /**
9607      * Shows the dialog.
9608      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9609      * @return {Roo.BasicDialog} this
9610      */
9611     show : function(animateTarget){
9612         if (this.fireEvent("beforeshow", this) === false){
9613             return;
9614         }
9615         if(this.syncHeightBeforeShow){
9616             this.syncBodyHeight();
9617         }else if(this.firstShow){
9618             this.firstShow = false;
9619             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9620         }
9621         this.animateTarget = animateTarget || this.animateTarget;
9622         if(!this.el.isVisible()){
9623             this.beforeShow();
9624             if(this.animateTarget && Roo.get(this.animateTarget)){
9625                 this.animShow();
9626             }else{
9627                 this.showEl();
9628             }
9629         }
9630         return this;
9631     },
9632
9633     // private
9634     showEl : function(){
9635         this.proxy.hide();
9636         this.el.setXY(this.xy);
9637         this.el.show();
9638         this.adjustAssets(true);
9639         this.toFront();
9640         this.focus();
9641         // IE peekaboo bug - fix found by Dave Fenwick
9642         if(Roo.isIE){
9643             this.el.repaint();
9644         }
9645         this.fireEvent("show", this);
9646     },
9647
9648     /**
9649      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
9650      * dialog itself will receive focus.
9651      */
9652     focus : function(){
9653         if(this.defaultButton){
9654             this.defaultButton.focus();
9655         }else{
9656             this.focusEl.focus();
9657         }
9658     },
9659
9660     // private
9661     constrainXY : function(){
9662         if(this.constraintoviewport !== false){
9663             if(!this.viewSize){
9664                 if(this.container){
9665                     var s = this.container.getSize();
9666                     this.viewSize = [s.width, s.height];
9667                 }else{
9668                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9669                 }
9670             }
9671             var s = Roo.get(this.container||document).getScroll();
9672
9673             var x = this.xy[0], y = this.xy[1];
9674             var w = this.size.width, h = this.size.height;
9675             var vw = this.viewSize[0], vh = this.viewSize[1];
9676             // only move it if it needs it
9677             var moved = false;
9678             // first validate right/bottom
9679             if(x + w > vw+s.left){
9680                 x = vw - w;
9681                 moved = true;
9682             }
9683             if(y + h > vh+s.top){
9684                 y = vh - h;
9685                 moved = true;
9686             }
9687             // then make sure top/left isn't negative
9688             if(x < s.left){
9689                 x = s.left;
9690                 moved = true;
9691             }
9692             if(y < s.top){
9693                 y = s.top;
9694                 moved = true;
9695             }
9696             if(moved){
9697                 // cache xy
9698                 this.xy = [x, y];
9699                 if(this.isVisible()){
9700                     this.el.setLocation(x, y);
9701                     this.adjustAssets();
9702                 }
9703             }
9704         }
9705     },
9706
9707     // private
9708     onDrag : function(){
9709         if(!this.proxyDrag){
9710             this.xy = this.el.getXY();
9711             this.adjustAssets();
9712         }
9713     },
9714
9715     // private
9716     adjustAssets : function(doShow){
9717         var x = this.xy[0], y = this.xy[1];
9718         var w = this.size.width, h = this.size.height;
9719         if(doShow === true){
9720             if(this.shadow){
9721                 this.shadow.show(this.el);
9722             }
9723             if(this.shim){
9724                 this.shim.show();
9725             }
9726         }
9727         if(this.shadow && this.shadow.isVisible()){
9728             this.shadow.show(this.el);
9729         }
9730         if(this.shim && this.shim.isVisible()){
9731             this.shim.setBounds(x, y, w, h);
9732         }
9733     },
9734
9735     // private
9736     adjustViewport : function(w, h){
9737         if(!w || !h){
9738             w = Roo.lib.Dom.getViewWidth();
9739             h = Roo.lib.Dom.getViewHeight();
9740         }
9741         // cache the size
9742         this.viewSize = [w, h];
9743         if(this.modal && this.mask.isVisible()){
9744             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9745             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9746         }
9747         if(this.isVisible()){
9748             this.constrainXY();
9749         }
9750     },
9751
9752     /**
9753      * Destroys this dialog and all its supporting elements (including any tabs, shim,
9754      * shadow, proxy, mask, etc.)  Also removes all event listeners.
9755      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9756      */
9757     destroy : function(removeEl){
9758         if(this.isVisible()){
9759             this.animateTarget = null;
9760             this.hide();
9761         }
9762         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9763         if(this.tabs){
9764             this.tabs.destroy(removeEl);
9765         }
9766         Roo.destroy(
9767              this.shim,
9768              this.proxy,
9769              this.resizer,
9770              this.close,
9771              this.mask
9772         );
9773         if(this.dd){
9774             this.dd.unreg();
9775         }
9776         if(this.buttons){
9777            for(var i = 0, len = this.buttons.length; i < len; i++){
9778                this.buttons[i].destroy();
9779            }
9780         }
9781         this.el.removeAllListeners();
9782         if(removeEl === true){
9783             this.el.update("");
9784             this.el.remove();
9785         }
9786         Roo.DialogManager.unregister(this);
9787     },
9788
9789     // private
9790     startMove : function(){
9791         if(this.proxyDrag){
9792             this.proxy.show();
9793         }
9794         if(this.constraintoviewport !== false){
9795             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9796         }
9797     },
9798
9799     // private
9800     endMove : function(){
9801         if(!this.proxyDrag){
9802             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9803         }else{
9804             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9805             this.proxy.hide();
9806         }
9807         this.refreshSize();
9808         this.adjustAssets();
9809         this.focus();
9810         this.fireEvent("move", this, this.xy[0], this.xy[1]);
9811     },
9812
9813     /**
9814      * Brings this dialog to the front of any other visible dialogs
9815      * @return {Roo.BasicDialog} this
9816      */
9817     toFront : function(){
9818         Roo.DialogManager.bringToFront(this);
9819         return this;
9820     },
9821
9822     /**
9823      * Sends this dialog to the back (under) of any other visible dialogs
9824      * @return {Roo.BasicDialog} this
9825      */
9826     toBack : function(){
9827         Roo.DialogManager.sendToBack(this);
9828         return this;
9829     },
9830
9831     /**
9832      * Centers this dialog in the viewport
9833      * @return {Roo.BasicDialog} this
9834      */
9835     center : function(){
9836         var xy = this.el.getCenterXY(true);
9837         this.moveTo(xy[0], xy[1]);
9838         return this;
9839     },
9840
9841     /**
9842      * Moves the dialog's top-left corner to the specified point
9843      * @param {Number} x
9844      * @param {Number} y
9845      * @return {Roo.BasicDialog} this
9846      */
9847     moveTo : function(x, y){
9848         this.xy = [x,y];
9849         if(this.isVisible()){
9850             this.el.setXY(this.xy);
9851             this.adjustAssets();
9852         }
9853         return this;
9854     },
9855
9856     /**
9857      * Aligns the dialog to the specified element
9858      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9859      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9860      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9861      * @return {Roo.BasicDialog} this
9862      */
9863     alignTo : function(element, position, offsets){
9864         this.xy = this.el.getAlignToXY(element, position, offsets);
9865         if(this.isVisible()){
9866             this.el.setXY(this.xy);
9867             this.adjustAssets();
9868         }
9869         return this;
9870     },
9871
9872     /**
9873      * Anchors an element to another element and realigns it when the window is resized.
9874      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9875      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9876      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9877      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9878      * is a number, it is used as the buffer delay (defaults to 50ms).
9879      * @return {Roo.BasicDialog} this
9880      */
9881     anchorTo : function(el, alignment, offsets, monitorScroll){
9882         var action = function(){
9883             this.alignTo(el, alignment, offsets);
9884         };
9885         Roo.EventManager.onWindowResize(action, this);
9886         var tm = typeof monitorScroll;
9887         if(tm != 'undefined'){
9888             Roo.EventManager.on(window, 'scroll', action, this,
9889                 {buffer: tm == 'number' ? monitorScroll : 50});
9890         }
9891         action.call(this);
9892         return this;
9893     },
9894
9895     /**
9896      * Returns true if the dialog is visible
9897      * @return {Boolean}
9898      */
9899     isVisible : function(){
9900         return this.el.isVisible();
9901     },
9902
9903     // private
9904     animHide : function(callback){
9905         var b = Roo.get(this.animateTarget).getBox();
9906         this.proxy.show();
9907         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9908         this.el.hide();
9909         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9910                     this.hideEl.createDelegate(this, [callback]));
9911     },
9912
9913     /**
9914      * Hides the dialog.
9915      * @param {Function} callback (optional) Function to call when the dialog is hidden
9916      * @return {Roo.BasicDialog} this
9917      */
9918     hide : function(callback){
9919         if (this.fireEvent("beforehide", this) === false){
9920             return;
9921         }
9922         if(this.shadow){
9923             this.shadow.hide();
9924         }
9925         if(this.shim) {
9926           this.shim.hide();
9927         }
9928         // sometimes animateTarget seems to get set.. causing problems...
9929         // this just double checks..
9930         if(this.animateTarget && Roo.get(this.animateTarget)) {
9931            this.animHide(callback);
9932         }else{
9933             this.el.hide();
9934             this.hideEl(callback);
9935         }
9936         return this;
9937     },
9938
9939     // private
9940     hideEl : function(callback){
9941         this.proxy.hide();
9942         if(this.modal){
9943             this.mask.hide();
9944             Roo.get(document.body).removeClass("x-body-masked");
9945         }
9946         this.fireEvent("hide", this);
9947         if(typeof callback == "function"){
9948             callback();
9949         }
9950     },
9951
9952     // private
9953     hideAction : function(){
9954         this.setLeft("-10000px");
9955         this.setTop("-10000px");
9956         this.setStyle("visibility", "hidden");
9957     },
9958
9959     // private
9960     refreshSize : function(){
9961         this.size = this.el.getSize();
9962         this.xy = this.el.getXY();
9963         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
9964     },
9965
9966     // private
9967     // z-index is managed by the DialogManager and may be overwritten at any time
9968     setZIndex : function(index){
9969         if(this.modal){
9970             this.mask.setStyle("z-index", index);
9971         }
9972         if(this.shim){
9973             this.shim.setStyle("z-index", ++index);
9974         }
9975         if(this.shadow){
9976             this.shadow.setZIndex(++index);
9977         }
9978         this.el.setStyle("z-index", ++index);
9979         if(this.proxy){
9980             this.proxy.setStyle("z-index", ++index);
9981         }
9982         if(this.resizer){
9983             this.resizer.proxy.setStyle("z-index", ++index);
9984         }
9985
9986         this.lastZIndex = index;
9987     },
9988
9989     /**
9990      * Returns the element for this dialog
9991      * @return {Roo.Element} The underlying dialog Element
9992      */
9993     getEl : function(){
9994         return this.el;
9995     }
9996 });
9997
9998 /**
9999  * @class Roo.DialogManager
10000  * Provides global access to BasicDialogs that have been created and
10001  * support for z-indexing (layering) multiple open dialogs.
10002  */
10003 Roo.DialogManager = function(){
10004     var list = {};
10005     var accessList = [];
10006     var front = null;
10007
10008     // private
10009     var sortDialogs = function(d1, d2){
10010         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10011     };
10012
10013     // private
10014     var orderDialogs = function(){
10015         accessList.sort(sortDialogs);
10016         var seed = Roo.DialogManager.zseed;
10017         for(var i = 0, len = accessList.length; i < len; i++){
10018             var dlg = accessList[i];
10019             if(dlg){
10020                 dlg.setZIndex(seed + (i*10));
10021             }
10022         }
10023     };
10024
10025     return {
10026         /**
10027          * The starting z-index for BasicDialogs (defaults to 9000)
10028          * @type Number The z-index value
10029          */
10030         zseed : 9000,
10031
10032         // private
10033         register : function(dlg){
10034             list[dlg.id] = dlg;
10035             accessList.push(dlg);
10036         },
10037
10038         // private
10039         unregister : function(dlg){
10040             delete list[dlg.id];
10041             var i=0;
10042             var len=0;
10043             if(!accessList.indexOf){
10044                 for(  i = 0, len = accessList.length; i < len; i++){
10045                     if(accessList[i] == dlg){
10046                         accessList.splice(i, 1);
10047                         return;
10048                     }
10049                 }
10050             }else{
10051                  i = accessList.indexOf(dlg);
10052                 if(i != -1){
10053                     accessList.splice(i, 1);
10054                 }
10055             }
10056         },
10057
10058         /**
10059          * Gets a registered dialog by id
10060          * @param {String/Object} id The id of the dialog or a dialog
10061          * @return {Roo.BasicDialog} this
10062          */
10063         get : function(id){
10064             return typeof id == "object" ? id : list[id];
10065         },
10066
10067         /**
10068          * Brings the specified dialog to the front
10069          * @param {String/Object} dlg The id of the dialog or a dialog
10070          * @return {Roo.BasicDialog} this
10071          */
10072         bringToFront : function(dlg){
10073             dlg = this.get(dlg);
10074             if(dlg != front){
10075                 front = dlg;
10076                 dlg._lastAccess = new Date().getTime();
10077                 orderDialogs();
10078             }
10079             return dlg;
10080         },
10081
10082         /**
10083          * Sends the specified dialog to the back
10084          * @param {String/Object} dlg The id of the dialog or a dialog
10085          * @return {Roo.BasicDialog} this
10086          */
10087         sendToBack : function(dlg){
10088             dlg = this.get(dlg);
10089             dlg._lastAccess = -(new Date().getTime());
10090             orderDialogs();
10091             return dlg;
10092         },
10093
10094         /**
10095          * Hides all dialogs
10096          */
10097         hideAll : function(){
10098             for(var id in list){
10099                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10100                     list[id].hide();
10101                 }
10102             }
10103         }
10104     };
10105 }();
10106
10107 /**
10108  * @class Roo.LayoutDialog
10109  * @extends Roo.BasicDialog
10110  * @children Roo.ContentPanel
10111  * @parent builder none
10112  * Dialog which provides adjustments for working with a layout in a Dialog.
10113  * Add your necessary layout config options to the dialog's config.<br>
10114  * Example usage (including a nested layout):
10115  * <pre><code>
10116 if(!dialog){
10117     dialog = new Roo.LayoutDialog("download-dlg", {
10118         modal: true,
10119         width:600,
10120         height:450,
10121         shadow:true,
10122         minWidth:500,
10123         minHeight:350,
10124         autoTabs:true,
10125         proxyDrag:true,
10126         // layout config merges with the dialog config
10127         center:{
10128             tabPosition: "top",
10129             alwaysShowTabs: true
10130         }
10131     });
10132     dialog.addKeyListener(27, dialog.hide, dialog);
10133     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10134     dialog.addButton("Build It!", this.getDownload, this);
10135
10136     // we can even add nested layouts
10137     var innerLayout = new Roo.BorderLayout("dl-inner", {
10138         east: {
10139             initialSize: 200,
10140             autoScroll:true,
10141             split:true
10142         },
10143         center: {
10144             autoScroll:true
10145         }
10146     });
10147     innerLayout.beginUpdate();
10148     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10149     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10150     innerLayout.endUpdate(true);
10151
10152     var layout = dialog.getLayout();
10153     layout.beginUpdate();
10154     layout.add("center", new Roo.ContentPanel("standard-panel",
10155                         {title: "Download the Source", fitToFrame:true}));
10156     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10157                {title: "Build your own roo.js"}));
10158     layout.getRegion("center").showPanel(sp);
10159     layout.endUpdate();
10160 }
10161 </code></pre>
10162     * @constructor
10163     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10164     * @param {Object} config configuration options
10165   */
10166 Roo.LayoutDialog = function(el, cfg){
10167     
10168     var config=  cfg;
10169     if (typeof(cfg) == 'undefined') {
10170         config = Roo.apply({}, el);
10171         // not sure why we use documentElement here.. - it should always be body.
10172         // IE7 borks horribly if we use documentElement.
10173         // webkit also does not like documentElement - it creates a body element...
10174         el = Roo.get( document.body || document.documentElement ).createChild();
10175         //config.autoCreate = true;
10176     }
10177     
10178     
10179     config.autoTabs = false;
10180     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10181     this.body.setStyle({overflow:"hidden", position:"relative"});
10182     this.layout = new Roo.BorderLayout(this.body.dom, config);
10183     this.layout.monitorWindowResize = false;
10184     this.el.addClass("x-dlg-auto-layout");
10185     // fix case when center region overwrites center function
10186     this.center = Roo.BasicDialog.prototype.center;
10187     this.on("show", this.layout.layout, this.layout, true);
10188     if (config.items) {
10189         var xitems = config.items;
10190         delete config.items;
10191         Roo.each(xitems, this.addxtype, this);
10192     }
10193     
10194     
10195 };
10196 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10197     
10198     
10199     /**
10200      * @cfg {Roo.LayoutRegion} east  
10201      */
10202     /**
10203      * @cfg {Roo.LayoutRegion} west
10204      */
10205     /**
10206      * @cfg {Roo.LayoutRegion} south
10207      */
10208     /**
10209      * @cfg {Roo.LayoutRegion} north
10210      */
10211     /**
10212      * @cfg {Roo.LayoutRegion} center
10213      */
10214     /**
10215      * @cfg {Roo.Button} buttons[]  Bottom buttons..
10216      */
10217     
10218     
10219     /**
10220      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10221      * @deprecated
10222      */
10223     endUpdate : function(){
10224         this.layout.endUpdate();
10225     },
10226
10227     /**
10228      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10229      *  @deprecated
10230      */
10231     beginUpdate : function(){
10232         this.layout.beginUpdate();
10233     },
10234
10235     /**
10236      * Get the BorderLayout for this dialog
10237      * @return {Roo.BorderLayout}
10238      */
10239     getLayout : function(){
10240         return this.layout;
10241     },
10242
10243     showEl : function(){
10244         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10245         if(Roo.isIE7){
10246             this.layout.layout();
10247         }
10248     },
10249
10250     // private
10251     // Use the syncHeightBeforeShow config option to control this automatically
10252     syncBodyHeight : function(){
10253         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10254         if(this.layout){this.layout.layout();}
10255     },
10256     
10257       /**
10258      * Add an xtype element (actually adds to the layout.)
10259      * @return {Object} xdata xtype object data.
10260      */
10261     
10262     addxtype : function(c) {
10263         return this.layout.addxtype(c);
10264     }
10265 });/*
10266  * Based on:
10267  * Ext JS Library 1.1.1
10268  * Copyright(c) 2006-2007, Ext JS, LLC.
10269  *
10270  * Originally Released Under LGPL - original licence link has changed is not relivant.
10271  *
10272  * Fork - LGPL
10273  * <script type="text/javascript">
10274  */
10275  
10276 /**
10277  * @class Roo.MessageBox
10278  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10279  * Example usage:
10280  *<pre><code>
10281 // Basic alert:
10282 Roo.Msg.alert('Status', 'Changes saved successfully.');
10283
10284 // Prompt for user data:
10285 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10286     if (btn == 'ok'){
10287         // process text value...
10288     }
10289 });
10290
10291 // Show a dialog using config options:
10292 Roo.Msg.show({
10293    title:'Save Changes?',
10294    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10295    buttons: Roo.Msg.YESNOCANCEL,
10296    fn: processResult,
10297    animEl: 'elId'
10298 });
10299 </code></pre>
10300  * @static
10301  */
10302 Roo.MessageBox = function(){
10303     var dlg, opt, mask, waitTimer;
10304     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10305     var buttons, activeTextEl, bwidth;
10306
10307     // private
10308     var handleButton = function(button){
10309         dlg.hide();
10310         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10311     };
10312
10313     // private
10314     var handleHide = function(){
10315         if(opt && opt.cls){
10316             dlg.el.removeClass(opt.cls);
10317         }
10318         if(waitTimer){
10319             Roo.TaskMgr.stop(waitTimer);
10320             waitTimer = null;
10321         }
10322     };
10323
10324     // private
10325     var updateButtons = function(b){
10326         var width = 0;
10327         if(!b){
10328             buttons["ok"].hide();
10329             buttons["cancel"].hide();
10330             buttons["yes"].hide();
10331             buttons["no"].hide();
10332             dlg.footer.dom.style.display = 'none';
10333             return width;
10334         }
10335         dlg.footer.dom.style.display = '';
10336         for(var k in buttons){
10337             if(typeof buttons[k] != "function"){
10338                 if(b[k]){
10339                     buttons[k].show();
10340                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10341                     width += buttons[k].el.getWidth()+15;
10342                 }else{
10343                     buttons[k].hide();
10344                 }
10345             }
10346         }
10347         return width;
10348     };
10349
10350     // private
10351     var handleEsc = function(d, k, e){
10352         if(opt && opt.closable !== false){
10353             dlg.hide();
10354         }
10355         if(e){
10356             e.stopEvent();
10357         }
10358     };
10359
10360     return {
10361         /**
10362          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10363          * @return {Roo.BasicDialog} The BasicDialog element
10364          */
10365         getDialog : function(){
10366            if(!dlg){
10367                 dlg = new Roo.BasicDialog("x-msg-box", {
10368                     autoCreate : true,
10369                     shadow: true,
10370                     draggable: true,
10371                     resizable:false,
10372                     constraintoviewport:false,
10373                     fixedcenter:true,
10374                     collapsible : false,
10375                     shim:true,
10376                     modal: true,
10377                     width:400, height:100,
10378                     buttonAlign:"center",
10379                     closeClick : function(){
10380                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10381                             handleButton("no");
10382                         }else{
10383                             handleButton("cancel");
10384                         }
10385                     }
10386                 });
10387                 dlg.on("hide", handleHide);
10388                 mask = dlg.mask;
10389                 dlg.addKeyListener(27, handleEsc);
10390                 buttons = {};
10391                 var bt = this.buttonText;
10392                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10393                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10394                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10395                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10396                 bodyEl = dlg.body.createChild({
10397
10398                     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>'
10399                 });
10400                 msgEl = bodyEl.dom.firstChild;
10401                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10402                 textboxEl.enableDisplayMode();
10403                 textboxEl.addKeyListener([10,13], function(){
10404                     if(dlg.isVisible() && opt && opt.buttons){
10405                         if(opt.buttons.ok){
10406                             handleButton("ok");
10407                         }else if(opt.buttons.yes){
10408                             handleButton("yes");
10409                         }
10410                     }
10411                 });
10412                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10413                 textareaEl.enableDisplayMode();
10414                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10415                 progressEl.enableDisplayMode();
10416                 var pf = progressEl.dom.firstChild;
10417                 if (pf) {
10418                     pp = Roo.get(pf.firstChild);
10419                     pp.setHeight(pf.offsetHeight);
10420                 }
10421                 
10422             }
10423             return dlg;
10424         },
10425
10426         /**
10427          * Updates the message box body text
10428          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10429          * the XHTML-compliant non-breaking space character '&amp;#160;')
10430          * @return {Roo.MessageBox} This message box
10431          */
10432         updateText : function(text){
10433             if(!dlg.isVisible() && !opt.width){
10434                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10435             }
10436             msgEl.innerHTML = text || '&#160;';
10437       
10438             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10439             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10440             var w = Math.max(
10441                     Math.min(opt.width || cw , this.maxWidth), 
10442                     Math.max(opt.minWidth || this.minWidth, bwidth)
10443             );
10444             if(opt.prompt){
10445                 activeTextEl.setWidth(w);
10446             }
10447             if(dlg.isVisible()){
10448                 dlg.fixedcenter = false;
10449             }
10450             // to big, make it scroll. = But as usual stupid IE does not support
10451             // !important..
10452             
10453             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10454                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10455                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10456             } else {
10457                 bodyEl.dom.style.height = '';
10458                 bodyEl.dom.style.overflowY = '';
10459             }
10460             if (cw > w) {
10461                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10462             } else {
10463                 bodyEl.dom.style.overflowX = '';
10464             }
10465             
10466             dlg.setContentSize(w, bodyEl.getHeight());
10467             if(dlg.isVisible()){
10468                 dlg.fixedcenter = true;
10469             }
10470             return this;
10471         },
10472
10473         /**
10474          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10475          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10476          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10477          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10478          * @return {Roo.MessageBox} This message box
10479          */
10480         updateProgress : function(value, text){
10481             if(text){
10482                 this.updateText(text);
10483             }
10484             if (pp) { // weird bug on my firefox - for some reason this is not defined
10485                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10486             }
10487             return this;
10488         },        
10489
10490         /**
10491          * Returns true if the message box is currently displayed
10492          * @return {Boolean} True if the message box is visible, else false
10493          */
10494         isVisible : function(){
10495             return dlg && dlg.isVisible();  
10496         },
10497
10498         /**
10499          * Hides the message box if it is displayed
10500          */
10501         hide : function(){
10502             if(this.isVisible()){
10503                 dlg.hide();
10504             }  
10505         },
10506
10507         /**
10508          * Displays a new message box, or reinitializes an existing message box, based on the config options
10509          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10510          * The following config object properties are supported:
10511          * <pre>
10512 Property    Type             Description
10513 ----------  ---------------  ------------------------------------------------------------------------------------
10514 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10515                                    closes (defaults to undefined)
10516 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10517                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10518 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10519                                    progress and wait dialogs will ignore this property and always hide the
10520                                    close button as they can only be closed programmatically.
10521 cls               String           A custom CSS class to apply to the message box element
10522 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10523                                    displayed (defaults to 75)
10524 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10525                                    function will be btn (the name of the button that was clicked, if applicable,
10526                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10527                                    Progress and wait dialogs will ignore this option since they do not respond to
10528                                    user actions and can only be closed programmatically, so any required function
10529                                    should be called by the same code after it closes the dialog.
10530 icon              String           A CSS class that provides a background image to be used as an icon for
10531                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10532 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10533 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10534 modal             Boolean          False to allow user interaction with the page while the message box is
10535                                    displayed (defaults to true)
10536 msg               String           A string that will replace the existing message box body text (defaults
10537                                    to the XHTML-compliant non-breaking space character '&#160;')
10538 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10539 progress          Boolean          True to display a progress bar (defaults to false)
10540 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10541 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10542 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10543 title             String           The title text
10544 value             String           The string value to set into the active textbox element if displayed
10545 wait              Boolean          True to display a progress bar (defaults to false)
10546 width             Number           The width of the dialog in pixels
10547 </pre>
10548          *
10549          * Example usage:
10550          * <pre><code>
10551 Roo.Msg.show({
10552    title: 'Address',
10553    msg: 'Please enter your address:',
10554    width: 300,
10555    buttons: Roo.MessageBox.OKCANCEL,
10556    multiline: true,
10557    fn: saveAddress,
10558    animEl: 'addAddressBtn'
10559 });
10560 </code></pre>
10561          * @param {Object} config Configuration options
10562          * @return {Roo.MessageBox} This message box
10563          */
10564         show : function(options)
10565         {
10566             
10567             // this causes nightmares if you show one dialog after another
10568             // especially on callbacks..
10569              
10570             if(this.isVisible()){
10571                 
10572                 this.hide();
10573                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10574                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10575                 Roo.log("New Dialog Message:" +  options.msg )
10576                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10577                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10578                 
10579             }
10580             var d = this.getDialog();
10581             opt = options;
10582             d.setTitle(opt.title || "&#160;");
10583             d.close.setDisplayed(opt.closable !== false);
10584             activeTextEl = textboxEl;
10585             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10586             if(opt.prompt){
10587                 if(opt.multiline){
10588                     textboxEl.hide();
10589                     textareaEl.show();
10590                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10591                         opt.multiline : this.defaultTextHeight);
10592                     activeTextEl = textareaEl;
10593                 }else{
10594                     textboxEl.show();
10595                     textareaEl.hide();
10596                 }
10597             }else{
10598                 textboxEl.hide();
10599                 textareaEl.hide();
10600             }
10601             progressEl.setDisplayed(opt.progress === true);
10602             this.updateProgress(0);
10603             activeTextEl.dom.value = opt.value || "";
10604             if(opt.prompt){
10605                 dlg.setDefaultButton(activeTextEl);
10606             }else{
10607                 var bs = opt.buttons;
10608                 var db = null;
10609                 if(bs && bs.ok){
10610                     db = buttons["ok"];
10611                 }else if(bs && bs.yes){
10612                     db = buttons["yes"];
10613                 }
10614                 dlg.setDefaultButton(db);
10615             }
10616             bwidth = updateButtons(opt.buttons);
10617             this.updateText(opt.msg);
10618             if(opt.cls){
10619                 d.el.addClass(opt.cls);
10620             }
10621             d.proxyDrag = opt.proxyDrag === true;
10622             d.modal = opt.modal !== false;
10623             d.mask = opt.modal !== false ? mask : false;
10624             if(!d.isVisible()){
10625                 // force it to the end of the z-index stack so it gets a cursor in FF
10626                 document.body.appendChild(dlg.el.dom);
10627                 d.animateTarget = null;
10628                 d.show(options.animEl);
10629             }
10630             return this;
10631         },
10632
10633         /**
10634          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10635          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10636          * and closing the message box when the process is complete.
10637          * @param {String} title The title bar text
10638          * @param {String} msg The message box body text
10639          * @return {Roo.MessageBox} This message box
10640          */
10641         progress : function(title, msg){
10642             this.show({
10643                 title : title,
10644                 msg : msg,
10645                 buttons: false,
10646                 progress:true,
10647                 closable:false,
10648                 minWidth: this.minProgressWidth,
10649                 modal : true
10650             });
10651             return this;
10652         },
10653
10654         /**
10655          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10656          * If a callback function is passed it will be called after the user clicks the button, and the
10657          * id of the button that was clicked will be passed as the only parameter to the callback
10658          * (could also be the top-right close button).
10659          * @param {String} title The title bar text
10660          * @param {String} msg The message box body text
10661          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10662          * @param {Object} scope (optional) The scope of the callback function
10663          * @return {Roo.MessageBox} This message box
10664          */
10665         alert : function(title, msg, fn, scope){
10666             this.show({
10667                 title : title,
10668                 msg : msg,
10669                 buttons: this.OK,
10670                 fn: fn,
10671                 scope : scope,
10672                 modal : true
10673             });
10674             return this;
10675         },
10676
10677         /**
10678          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10679          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10680          * You are responsible for closing the message box when the process is complete.
10681          * @param {String} msg The message box body text
10682          * @param {String} title (optional) The title bar text
10683          * @return {Roo.MessageBox} This message box
10684          */
10685         wait : function(msg, title){
10686             this.show({
10687                 title : title,
10688                 msg : msg,
10689                 buttons: false,
10690                 closable:false,
10691                 progress:true,
10692                 modal:true,
10693                 width:300,
10694                 wait:true
10695             });
10696             waitTimer = Roo.TaskMgr.start({
10697                 run: function(i){
10698                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10699                 },
10700                 interval: 1000
10701             });
10702             return this;
10703         },
10704
10705         /**
10706          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10707          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10708          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10709          * @param {String} title The title bar text
10710          * @param {String} msg The message box body text
10711          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10712          * @param {Object} scope (optional) The scope of the callback function
10713          * @return {Roo.MessageBox} This message box
10714          */
10715         confirm : function(title, msg, fn, scope){
10716             this.show({
10717                 title : title,
10718                 msg : msg,
10719                 buttons: this.YESNO,
10720                 fn: fn,
10721                 scope : scope,
10722                 modal : true
10723             });
10724             return this;
10725         },
10726
10727         /**
10728          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10729          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10730          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10731          * (could also be the top-right close button) and the text that was entered will be passed as the two
10732          * parameters to the callback.
10733          * @param {String} title The title bar text
10734          * @param {String} msg The message box body text
10735          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10736          * @param {Object} scope (optional) The scope of the callback function
10737          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10738          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10739          * @return {Roo.MessageBox} This message box
10740          */
10741         prompt : function(title, msg, fn, scope, multiline){
10742             this.show({
10743                 title : title,
10744                 msg : msg,
10745                 buttons: this.OKCANCEL,
10746                 fn: fn,
10747                 minWidth:250,
10748                 scope : scope,
10749                 prompt:true,
10750                 multiline: multiline,
10751                 modal : true
10752             });
10753             return this;
10754         },
10755
10756         /**
10757          * Button config that displays a single OK button
10758          * @type Object
10759          */
10760         OK : {ok:true},
10761         /**
10762          * Button config that displays Yes and No buttons
10763          * @type Object
10764          */
10765         YESNO : {yes:true, no:true},
10766         /**
10767          * Button config that displays OK and Cancel buttons
10768          * @type Object
10769          */
10770         OKCANCEL : {ok:true, cancel:true},
10771         /**
10772          * Button config that displays Yes, No and Cancel buttons
10773          * @type Object
10774          */
10775         YESNOCANCEL : {yes:true, no:true, cancel:true},
10776
10777         /**
10778          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10779          * @type Number
10780          */
10781         defaultTextHeight : 75,
10782         /**
10783          * The maximum width in pixels of the message box (defaults to 600)
10784          * @type Number
10785          */
10786         maxWidth : 600,
10787         /**
10788          * The minimum width in pixels of the message box (defaults to 100)
10789          * @type Number
10790          */
10791         minWidth : 100,
10792         /**
10793          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10794          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10795          * @type Number
10796          */
10797         minProgressWidth : 250,
10798         /**
10799          * An object containing the default button text strings that can be overriden for localized language support.
10800          * Supported properties are: ok, cancel, yes and no.
10801          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10802          * @type Object
10803          */
10804         buttonText : {
10805             ok : "OK",
10806             cancel : "Cancel",
10807             yes : "Yes",
10808             no : "No"
10809         }
10810     };
10811 }();
10812
10813 /**
10814  * Shorthand for {@link Roo.MessageBox}
10815  */
10816 Roo.Msg = Roo.MessageBox;/*
10817  * Based on:
10818  * Ext JS Library 1.1.1
10819  * Copyright(c) 2006-2007, Ext JS, LLC.
10820  *
10821  * Originally Released Under LGPL - original licence link has changed is not relivant.
10822  *
10823  * Fork - LGPL
10824  * <script type="text/javascript">
10825  */
10826 /**
10827  * @class Roo.QuickTips
10828  * Provides attractive and customizable tooltips for any element.
10829  * @static
10830  */
10831 Roo.QuickTips = function(){
10832     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10833     var ce, bd, xy, dd;
10834     var visible = false, disabled = true, inited = false;
10835     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10836     
10837     var onOver = function(e){
10838         if(disabled){
10839             return;
10840         }
10841         var t = e.getTarget();
10842         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10843             return;
10844         }
10845         if(ce && t == ce.el){
10846             clearTimeout(hideProc);
10847             return;
10848         }
10849         if(t && tagEls[t.id]){
10850             tagEls[t.id].el = t;
10851             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10852             return;
10853         }
10854         var ttp, et = Roo.fly(t);
10855         var ns = cfg.namespace;
10856         if(tm.interceptTitles && t.title){
10857             ttp = t.title;
10858             t.qtip = ttp;
10859             t.removeAttribute("title");
10860             e.preventDefault();
10861         }else{
10862             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10863         }
10864         if(ttp){
10865             showProc = show.defer(tm.showDelay, tm, [{
10866                 el: t, 
10867                 text: ttp.replace(/\\n/g,'<br/>'),
10868                 width: et.getAttributeNS(ns, cfg.width),
10869                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10870                 title: et.getAttributeNS(ns, cfg.title),
10871                     cls: et.getAttributeNS(ns, cfg.cls)
10872             }]);
10873         }
10874     };
10875     
10876     var onOut = function(e){
10877         clearTimeout(showProc);
10878         var t = e.getTarget();
10879         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10880             hideProc = setTimeout(hide, tm.hideDelay);
10881         }
10882     };
10883     
10884     var onMove = function(e){
10885         if(disabled){
10886             return;
10887         }
10888         xy = e.getXY();
10889         xy[1] += 18;
10890         if(tm.trackMouse && ce){
10891             el.setXY(xy);
10892         }
10893     };
10894     
10895     var onDown = function(e){
10896         clearTimeout(showProc);
10897         clearTimeout(hideProc);
10898         if(!e.within(el)){
10899             if(tm.hideOnClick){
10900                 hide();
10901                 tm.disable();
10902                 tm.enable.defer(100, tm);
10903             }
10904         }
10905     };
10906     
10907     var getPad = function(){
10908         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10909     };
10910
10911     var show = function(o){
10912         if(disabled){
10913             return;
10914         }
10915         clearTimeout(dismissProc);
10916         ce = o;
10917         if(removeCls){ // in case manually hidden
10918             el.removeClass(removeCls);
10919             removeCls = null;
10920         }
10921         if(ce.cls){
10922             el.addClass(ce.cls);
10923             removeCls = ce.cls;
10924         }
10925         if(ce.title){
10926             tipTitle.update(ce.title);
10927             tipTitle.show();
10928         }else{
10929             tipTitle.update('');
10930             tipTitle.hide();
10931         }
10932         el.dom.style.width  = tm.maxWidth+'px';
10933         //tipBody.dom.style.width = '';
10934         tipBodyText.update(o.text);
10935         var p = getPad(), w = ce.width;
10936         if(!w){
10937             var td = tipBodyText.dom;
10938             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10939             if(aw > tm.maxWidth){
10940                 w = tm.maxWidth;
10941             }else if(aw < tm.minWidth){
10942                 w = tm.minWidth;
10943             }else{
10944                 w = aw;
10945             }
10946         }
10947         //tipBody.setWidth(w);
10948         el.setWidth(parseInt(w, 10) + p);
10949         if(ce.autoHide === false){
10950             close.setDisplayed(true);
10951             if(dd){
10952                 dd.unlock();
10953             }
10954         }else{
10955             close.setDisplayed(false);
10956             if(dd){
10957                 dd.lock();
10958             }
10959         }
10960         if(xy){
10961             el.avoidY = xy[1]-18;
10962             el.setXY(xy);
10963         }
10964         if(tm.animate){
10965             el.setOpacity(.1);
10966             el.setStyle("visibility", "visible");
10967             el.fadeIn({callback: afterShow});
10968         }else{
10969             afterShow();
10970         }
10971     };
10972     
10973     var afterShow = function(){
10974         if(ce){
10975             el.show();
10976             esc.enable();
10977             if(tm.autoDismiss && ce.autoHide !== false){
10978                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
10979             }
10980         }
10981     };
10982     
10983     var hide = function(noanim){
10984         clearTimeout(dismissProc);
10985         clearTimeout(hideProc);
10986         ce = null;
10987         if(el.isVisible()){
10988             esc.disable();
10989             if(noanim !== true && tm.animate){
10990                 el.fadeOut({callback: afterHide});
10991             }else{
10992                 afterHide();
10993             } 
10994         }
10995     };
10996     
10997     var afterHide = function(){
10998         el.hide();
10999         if(removeCls){
11000             el.removeClass(removeCls);
11001             removeCls = null;
11002         }
11003     };
11004     
11005     return {
11006         /**
11007         * @cfg {Number} minWidth
11008         * The minimum width of the quick tip (defaults to 40)
11009         */
11010        minWidth : 40,
11011         /**
11012         * @cfg {Number} maxWidth
11013         * The maximum width of the quick tip (defaults to 300)
11014         */
11015        maxWidth : 300,
11016         /**
11017         * @cfg {Boolean} interceptTitles
11018         * True to automatically use the element's DOM title value if available (defaults to false)
11019         */
11020        interceptTitles : false,
11021         /**
11022         * @cfg {Boolean} trackMouse
11023         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11024         */
11025        trackMouse : false,
11026         /**
11027         * @cfg {Boolean} hideOnClick
11028         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11029         */
11030        hideOnClick : true,
11031         /**
11032         * @cfg {Number} showDelay
11033         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11034         */
11035        showDelay : 500,
11036         /**
11037         * @cfg {Number} hideDelay
11038         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11039         */
11040        hideDelay : 200,
11041         /**
11042         * @cfg {Boolean} autoHide
11043         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11044         * Used in conjunction with hideDelay.
11045         */
11046        autoHide : true,
11047         /**
11048         * @cfg {Boolean}
11049         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11050         * (defaults to true).  Used in conjunction with autoDismissDelay.
11051         */
11052        autoDismiss : true,
11053         /**
11054         * @cfg {Number}
11055         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11056         */
11057        autoDismissDelay : 5000,
11058        /**
11059         * @cfg {Boolean} animate
11060         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11061         */
11062        animate : false,
11063
11064        /**
11065         * @cfg {String} title
11066         * Title text to display (defaults to '').  This can be any valid HTML markup.
11067         */
11068         title: '',
11069        /**
11070         * @cfg {String} text
11071         * Body text to display (defaults to '').  This can be any valid HTML markup.
11072         */
11073         text : '',
11074        /**
11075         * @cfg {String} cls
11076         * A CSS class to apply to the base quick tip element (defaults to '').
11077         */
11078         cls : '',
11079        /**
11080         * @cfg {Number} width
11081         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11082         * minWidth or maxWidth.
11083         */
11084         width : null,
11085
11086     /**
11087      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11088      * or display QuickTips in a page.
11089      */
11090        init : function(){
11091           tm = Roo.QuickTips;
11092           cfg = tm.tagConfig;
11093           if(!inited){
11094               if(!Roo.isReady){ // allow calling of init() before onReady
11095                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11096                   return;
11097               }
11098               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11099               el.fxDefaults = {stopFx: true};
11100               // maximum custom styling
11101               //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>');
11102               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>');              
11103               tipTitle = el.child('h3');
11104               tipTitle.enableDisplayMode("block");
11105               tipBody = el.child('div.x-tip-bd');
11106               tipBodyText = el.child('div.x-tip-bd-inner');
11107               //bdLeft = el.child('div.x-tip-bd-left');
11108               //bdRight = el.child('div.x-tip-bd-right');
11109               close = el.child('div.x-tip-close');
11110               close.enableDisplayMode("block");
11111               close.on("click", hide);
11112               var d = Roo.get(document);
11113               d.on("mousedown", onDown);
11114               d.on("mouseover", onOver);
11115               d.on("mouseout", onOut);
11116               d.on("mousemove", onMove);
11117               esc = d.addKeyListener(27, hide);
11118               esc.disable();
11119               if(Roo.dd.DD){
11120                   dd = el.initDD("default", null, {
11121                       onDrag : function(){
11122                           el.sync();  
11123                       }
11124                   });
11125                   dd.setHandleElId(tipTitle.id);
11126                   dd.lock();
11127               }
11128               inited = true;
11129           }
11130           this.enable(); 
11131        },
11132
11133     /**
11134      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11135      * are supported:
11136      * <pre>
11137 Property    Type                   Description
11138 ----------  ---------------------  ------------------------------------------------------------------------
11139 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11140      * </ul>
11141      * @param {Object} config The config object
11142      */
11143        register : function(config){
11144            var cs = config instanceof Array ? config : arguments;
11145            for(var i = 0, len = cs.length; i < len; i++) {
11146                var c = cs[i];
11147                var target = c.target;
11148                if(target){
11149                    if(target instanceof Array){
11150                        for(var j = 0, jlen = target.length; j < jlen; j++){
11151                            tagEls[target[j]] = c;
11152                        }
11153                    }else{
11154                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11155                    }
11156                }
11157            }
11158        },
11159
11160     /**
11161      * Removes this quick tip from its element and destroys it.
11162      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11163      */
11164        unregister : function(el){
11165            delete tagEls[Roo.id(el)];
11166        },
11167
11168     /**
11169      * Enable this quick tip.
11170      */
11171        enable : function(){
11172            if(inited && disabled){
11173                locks.pop();
11174                if(locks.length < 1){
11175                    disabled = false;
11176                }
11177            }
11178        },
11179
11180     /**
11181      * Disable this quick tip.
11182      */
11183        disable : function(){
11184           disabled = true;
11185           clearTimeout(showProc);
11186           clearTimeout(hideProc);
11187           clearTimeout(dismissProc);
11188           if(ce){
11189               hide(true);
11190           }
11191           locks.push(1);
11192        },
11193
11194     /**
11195      * Returns true if the quick tip is enabled, else false.
11196      */
11197        isEnabled : function(){
11198             return !disabled;
11199        },
11200
11201         // private
11202        tagConfig : {
11203            namespace : "roo", // was ext?? this may break..
11204            alt_namespace : "ext",
11205            attribute : "qtip",
11206            width : "width",
11207            target : "target",
11208            title : "qtitle",
11209            hide : "hide",
11210            cls : "qclass"
11211        }
11212    };
11213 }();
11214
11215 // backwards compat
11216 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11217  * Based on:
11218  * Ext JS Library 1.1.1
11219  * Copyright(c) 2006-2007, Ext JS, LLC.
11220  *
11221  * Originally Released Under LGPL - original licence link has changed is not relivant.
11222  *
11223  * Fork - LGPL
11224  * <script type="text/javascript">
11225  */
11226  
11227
11228 /**
11229  * @class Roo.tree.TreePanel
11230  * @extends Roo.data.Tree
11231  * @cfg {Roo.tree.TreeNode} root The root node
11232  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11233  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11234  * @cfg {Boolean} enableDD true to enable drag and drop
11235  * @cfg {Boolean} enableDrag true to enable just drag
11236  * @cfg {Boolean} enableDrop true to enable just drop
11237  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11238  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11239  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11240  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11241  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11242  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11243  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11244  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11245  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11246  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11247  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11248  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
11249  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
11250  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11251  * @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>
11252  * @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>
11253  * 
11254  * @constructor
11255  * @param {String/HTMLElement/Element} el The container element
11256  * @param {Object} config
11257  */
11258 Roo.tree.TreePanel = function(el, config){
11259     var root = false;
11260     var loader = false;
11261     if (config.root) {
11262         root = config.root;
11263         delete config.root;
11264     }
11265     if (config.loader) {
11266         loader = config.loader;
11267         delete config.loader;
11268     }
11269     
11270     Roo.apply(this, config);
11271     Roo.tree.TreePanel.superclass.constructor.call(this);
11272     this.el = Roo.get(el);
11273     this.el.addClass('x-tree');
11274     //console.log(root);
11275     if (root) {
11276         this.setRootNode( Roo.factory(root, Roo.tree));
11277     }
11278     if (loader) {
11279         this.loader = Roo.factory(loader, Roo.tree);
11280     }
11281    /**
11282     * Read-only. The id of the container element becomes this TreePanel's id.
11283     */
11284     this.id = this.el.id;
11285     this.addEvents({
11286         /**
11287         * @event beforeload
11288         * Fires before a node is loaded, return false to cancel
11289         * @param {Node} node The node being loaded
11290         */
11291         "beforeload" : true,
11292         /**
11293         * @event load
11294         * Fires when a node is loaded
11295         * @param {Node} node The node that was loaded
11296         */
11297         "load" : true,
11298         /**
11299         * @event textchange
11300         * Fires when the text for a node is changed
11301         * @param {Node} node The node
11302         * @param {String} text The new text
11303         * @param {String} oldText The old text
11304         */
11305         "textchange" : true,
11306         /**
11307         * @event beforeexpand
11308         * Fires before a node is expanded, return false to cancel.
11309         * @param {Node} node The node
11310         * @param {Boolean} deep
11311         * @param {Boolean} anim
11312         */
11313         "beforeexpand" : true,
11314         /**
11315         * @event beforecollapse
11316         * Fires before a node is collapsed, return false to cancel.
11317         * @param {Node} node The node
11318         * @param {Boolean} deep
11319         * @param {Boolean} anim
11320         */
11321         "beforecollapse" : true,
11322         /**
11323         * @event expand
11324         * Fires when a node is expanded
11325         * @param {Node} node The node
11326         */
11327         "expand" : true,
11328         /**
11329         * @event disabledchange
11330         * Fires when the disabled status of a node changes
11331         * @param {Node} node The node
11332         * @param {Boolean} disabled
11333         */
11334         "disabledchange" : true,
11335         /**
11336         * @event collapse
11337         * Fires when a node is collapsed
11338         * @param {Node} node The node
11339         */
11340         "collapse" : true,
11341         /**
11342         * @event beforeclick
11343         * Fires before click processing on a node. Return false to cancel the default action.
11344         * @param {Node} node The node
11345         * @param {Roo.EventObject} e The event object
11346         */
11347         "beforeclick":true,
11348         /**
11349         * @event checkchange
11350         * Fires when a node with a checkbox's checked property changes
11351         * @param {Node} this This node
11352         * @param {Boolean} checked
11353         */
11354         "checkchange":true,
11355         /**
11356         * @event click
11357         * Fires when a node is clicked
11358         * @param {Node} node The node
11359         * @param {Roo.EventObject} e The event object
11360         */
11361         "click":true,
11362         /**
11363         * @event dblclick
11364         * Fires when a node is double clicked
11365         * @param {Node} node The node
11366         * @param {Roo.EventObject} e The event object
11367         */
11368         "dblclick":true,
11369         /**
11370         * @event contextmenu
11371         * Fires when a node is right clicked
11372         * @param {Node} node The node
11373         * @param {Roo.EventObject} e The event object
11374         */
11375         "contextmenu":true,
11376         /**
11377         * @event beforechildrenrendered
11378         * Fires right before the child nodes for a node are rendered
11379         * @param {Node} node The node
11380         */
11381         "beforechildrenrendered":true,
11382         /**
11383         * @event startdrag
11384         * Fires when a node starts being dragged
11385         * @param {Roo.tree.TreePanel} this
11386         * @param {Roo.tree.TreeNode} node
11387         * @param {event} e The raw browser event
11388         */ 
11389        "startdrag" : true,
11390        /**
11391         * @event enddrag
11392         * Fires when a drag operation is complete
11393         * @param {Roo.tree.TreePanel} this
11394         * @param {Roo.tree.TreeNode} node
11395         * @param {event} e The raw browser event
11396         */
11397        "enddrag" : true,
11398        /**
11399         * @event dragdrop
11400         * Fires when a dragged node is dropped on a valid DD target
11401         * @param {Roo.tree.TreePanel} this
11402         * @param {Roo.tree.TreeNode} node
11403         * @param {DD} dd The dd it was dropped on
11404         * @param {event} e The raw browser event
11405         */
11406        "dragdrop" : true,
11407        /**
11408         * @event beforenodedrop
11409         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11410         * passed to handlers has the following properties:<br />
11411         * <ul style="padding:5px;padding-left:16px;">
11412         * <li>tree - The TreePanel</li>
11413         * <li>target - The node being targeted for the drop</li>
11414         * <li>data - The drag data from the drag source</li>
11415         * <li>point - The point of the drop - append, above or below</li>
11416         * <li>source - The drag source</li>
11417         * <li>rawEvent - Raw mouse event</li>
11418         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11419         * to be inserted by setting them on this object.</li>
11420         * <li>cancel - Set this to true to cancel the drop.</li>
11421         * </ul>
11422         * @param {Object} dropEvent
11423         */
11424        "beforenodedrop" : true,
11425        /**
11426         * @event nodedrop
11427         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11428         * passed to handlers has the following properties:<br />
11429         * <ul style="padding:5px;padding-left:16px;">
11430         * <li>tree - The TreePanel</li>
11431         * <li>target - The node being targeted for the drop</li>
11432         * <li>data - The drag data from the drag source</li>
11433         * <li>point - The point of the drop - append, above or below</li>
11434         * <li>source - The drag source</li>
11435         * <li>rawEvent - Raw mouse event</li>
11436         * <li>dropNode - Dropped node(s).</li>
11437         * </ul>
11438         * @param {Object} dropEvent
11439         */
11440        "nodedrop" : true,
11441         /**
11442         * @event nodedragover
11443         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11444         * passed to handlers has the following properties:<br />
11445         * <ul style="padding:5px;padding-left:16px;">
11446         * <li>tree - The TreePanel</li>
11447         * <li>target - The node being targeted for the drop</li>
11448         * <li>data - The drag data from the drag source</li>
11449         * <li>point - The point of the drop - append, above or below</li>
11450         * <li>source - The drag source</li>
11451         * <li>rawEvent - Raw mouse event</li>
11452         * <li>dropNode - Drop node(s) provided by the source.</li>
11453         * <li>cancel - Set this to true to signal drop not allowed.</li>
11454         * </ul>
11455         * @param {Object} dragOverEvent
11456         */
11457        "nodedragover" : true,
11458        /**
11459         * @event appendnode
11460         * Fires when append node to the tree
11461         * @param {Roo.tree.TreePanel} this
11462         * @param {Roo.tree.TreeNode} node
11463         * @param {Number} index The index of the newly appended node
11464         */
11465        "appendnode" : true
11466         
11467     });
11468     if(this.singleExpand){
11469        this.on("beforeexpand", this.restrictExpand, this);
11470     }
11471     if (this.editor) {
11472         this.editor.tree = this;
11473         this.editor = Roo.factory(this.editor, Roo.tree);
11474     }
11475     
11476     if (this.selModel) {
11477         this.selModel = Roo.factory(this.selModel, Roo.tree);
11478     }
11479    
11480 };
11481 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11482     rootVisible : true,
11483     animate: Roo.enableFx,
11484     lines : true,
11485     enableDD : false,
11486     hlDrop : Roo.enableFx,
11487   
11488     renderer: false,
11489     
11490     rendererTip: false,
11491     // private
11492     restrictExpand : function(node){
11493         var p = node.parentNode;
11494         if(p){
11495             if(p.expandedChild && p.expandedChild.parentNode == p){
11496                 p.expandedChild.collapse();
11497             }
11498             p.expandedChild = node;
11499         }
11500     },
11501
11502     // private override
11503     setRootNode : function(node){
11504         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11505         if(!this.rootVisible){
11506             node.ui = new Roo.tree.RootTreeNodeUI(node);
11507         }
11508         return node;
11509     },
11510
11511     /**
11512      * Returns the container element for this TreePanel
11513      */
11514     getEl : function(){
11515         return this.el;
11516     },
11517
11518     /**
11519      * Returns the default TreeLoader for this TreePanel
11520      */
11521     getLoader : function(){
11522         return this.loader;
11523     },
11524
11525     /**
11526      * Expand all nodes
11527      */
11528     expandAll : function(){
11529         this.root.expand(true);
11530     },
11531
11532     /**
11533      * Collapse all nodes
11534      */
11535     collapseAll : function(){
11536         this.root.collapse(true);
11537     },
11538
11539     /**
11540      * Returns the selection model used by this TreePanel
11541      */
11542     getSelectionModel : function(){
11543         if(!this.selModel){
11544             this.selModel = new Roo.tree.DefaultSelectionModel();
11545         }
11546         return this.selModel;
11547     },
11548
11549     /**
11550      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11551      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11552      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11553      * @return {Array}
11554      */
11555     getChecked : function(a, startNode){
11556         startNode = startNode || this.root;
11557         var r = [];
11558         var f = function(){
11559             if(this.attributes.checked){
11560                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11561             }
11562         }
11563         startNode.cascade(f);
11564         return r;
11565     },
11566
11567     /**
11568      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11569      * @param {String} path
11570      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11571      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11572      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11573      */
11574     expandPath : function(path, attr, callback){
11575         attr = attr || "id";
11576         var keys = path.split(this.pathSeparator);
11577         var curNode = this.root;
11578         if(curNode.attributes[attr] != keys[1]){ // invalid root
11579             if(callback){
11580                 callback(false, null);
11581             }
11582             return;
11583         }
11584         var index = 1;
11585         var f = function(){
11586             if(++index == keys.length){
11587                 if(callback){
11588                     callback(true, curNode);
11589                 }
11590                 return;
11591             }
11592             var c = curNode.findChild(attr, keys[index]);
11593             if(!c){
11594                 if(callback){
11595                     callback(false, curNode);
11596                 }
11597                 return;
11598             }
11599             curNode = c;
11600             c.expand(false, false, f);
11601         };
11602         curNode.expand(false, false, f);
11603     },
11604
11605     /**
11606      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11607      * @param {String} path
11608      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11609      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11610      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11611      */
11612     selectPath : function(path, attr, callback){
11613         attr = attr || "id";
11614         var keys = path.split(this.pathSeparator);
11615         var v = keys.pop();
11616         if(keys.length > 0){
11617             var f = function(success, node){
11618                 if(success && node){
11619                     var n = node.findChild(attr, v);
11620                     if(n){
11621                         n.select();
11622                         if(callback){
11623                             callback(true, n);
11624                         }
11625                     }else if(callback){
11626                         callback(false, n);
11627                     }
11628                 }else{
11629                     if(callback){
11630                         callback(false, n);
11631                     }
11632                 }
11633             };
11634             this.expandPath(keys.join(this.pathSeparator), attr, f);
11635         }else{
11636             this.root.select();
11637             if(callback){
11638                 callback(true, this.root);
11639             }
11640         }
11641     },
11642
11643     getTreeEl : function(){
11644         return this.el;
11645     },
11646
11647     /**
11648      * Trigger rendering of this TreePanel
11649      */
11650     render : function(){
11651         if (this.innerCt) {
11652             return this; // stop it rendering more than once!!
11653         }
11654         
11655         this.innerCt = this.el.createChild({tag:"ul",
11656                cls:"x-tree-root-ct " +
11657                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11658
11659         if(this.containerScroll){
11660             Roo.dd.ScrollManager.register(this.el);
11661         }
11662         if((this.enableDD || this.enableDrop) && !this.dropZone){
11663            /**
11664             * The dropZone used by this tree if drop is enabled
11665             * @type Roo.tree.TreeDropZone
11666             */
11667              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11668                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11669            });
11670         }
11671         if((this.enableDD || this.enableDrag) && !this.dragZone){
11672            /**
11673             * The dragZone used by this tree if drag is enabled
11674             * @type Roo.tree.TreeDragZone
11675             */
11676             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11677                ddGroup: this.ddGroup || "TreeDD",
11678                scroll: this.ddScroll
11679            });
11680         }
11681         this.getSelectionModel().init(this);
11682         if (!this.root) {
11683             Roo.log("ROOT not set in tree");
11684             return this;
11685         }
11686         this.root.render();
11687         if(!this.rootVisible){
11688             this.root.renderChildren();
11689         }
11690         return this;
11691     }
11692 });/*
11693  * Based on:
11694  * Ext JS Library 1.1.1
11695  * Copyright(c) 2006-2007, Ext JS, LLC.
11696  *
11697  * Originally Released Under LGPL - original licence link has changed is not relivant.
11698  *
11699  * Fork - LGPL
11700  * <script type="text/javascript">
11701  */
11702  
11703
11704 /**
11705  * @class Roo.tree.DefaultSelectionModel
11706  * @extends Roo.util.Observable
11707  * The default single selection for a TreePanel.
11708  * @param {Object} cfg Configuration
11709  */
11710 Roo.tree.DefaultSelectionModel = function(cfg){
11711    this.selNode = null;
11712    
11713    
11714    
11715    this.addEvents({
11716        /**
11717         * @event selectionchange
11718         * Fires when the selected node changes
11719         * @param {DefaultSelectionModel} this
11720         * @param {TreeNode} node the new selection
11721         */
11722        "selectionchange" : true,
11723
11724        /**
11725         * @event beforeselect
11726         * Fires before the selected node changes, return false to cancel the change
11727         * @param {DefaultSelectionModel} this
11728         * @param {TreeNode} node the new selection
11729         * @param {TreeNode} node the old selection
11730         */
11731        "beforeselect" : true
11732    });
11733    
11734     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11735 };
11736
11737 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11738     init : function(tree){
11739         this.tree = tree;
11740         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11741         tree.on("click", this.onNodeClick, this);
11742     },
11743     
11744     onNodeClick : function(node, e){
11745         if (e.ctrlKey && this.selNode == node)  {
11746             this.unselect(node);
11747             return;
11748         }
11749         this.select(node);
11750     },
11751     
11752     /**
11753      * Select a node.
11754      * @param {TreeNode} node The node to select
11755      * @return {TreeNode} The selected node
11756      */
11757     select : function(node){
11758         var last = this.selNode;
11759         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11760             if(last){
11761                 last.ui.onSelectedChange(false);
11762             }
11763             this.selNode = node;
11764             node.ui.onSelectedChange(true);
11765             this.fireEvent("selectionchange", this, node, last);
11766         }
11767         return node;
11768     },
11769     
11770     /**
11771      * Deselect a node.
11772      * @param {TreeNode} node The node to unselect
11773      */
11774     unselect : function(node){
11775         if(this.selNode == node){
11776             this.clearSelections();
11777         }    
11778     },
11779     
11780     /**
11781      * Clear all selections
11782      */
11783     clearSelections : function(){
11784         var n = this.selNode;
11785         if(n){
11786             n.ui.onSelectedChange(false);
11787             this.selNode = null;
11788             this.fireEvent("selectionchange", this, null);
11789         }
11790         return n;
11791     },
11792     
11793     /**
11794      * Get the selected node
11795      * @return {TreeNode} The selected node
11796      */
11797     getSelectedNode : function(){
11798         return this.selNode;    
11799     },
11800     
11801     /**
11802      * Returns true if the node is selected
11803      * @param {TreeNode} node The node to check
11804      * @return {Boolean}
11805      */
11806     isSelected : function(node){
11807         return this.selNode == node;  
11808     },
11809
11810     /**
11811      * Selects the node above the selected node in the tree, intelligently walking the nodes
11812      * @return TreeNode The new selection
11813      */
11814     selectPrevious : function(){
11815         var s = this.selNode || this.lastSelNode;
11816         if(!s){
11817             return null;
11818         }
11819         var ps = s.previousSibling;
11820         if(ps){
11821             if(!ps.isExpanded() || ps.childNodes.length < 1){
11822                 return this.select(ps);
11823             } else{
11824                 var lc = ps.lastChild;
11825                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11826                     lc = lc.lastChild;
11827                 }
11828                 return this.select(lc);
11829             }
11830         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11831             return this.select(s.parentNode);
11832         }
11833         return null;
11834     },
11835
11836     /**
11837      * Selects the node above the selected node in the tree, intelligently walking the nodes
11838      * @return TreeNode The new selection
11839      */
11840     selectNext : function(){
11841         var s = this.selNode || this.lastSelNode;
11842         if(!s){
11843             return null;
11844         }
11845         if(s.firstChild && s.isExpanded()){
11846              return this.select(s.firstChild);
11847          }else if(s.nextSibling){
11848              return this.select(s.nextSibling);
11849          }else if(s.parentNode){
11850             var newS = null;
11851             s.parentNode.bubble(function(){
11852                 if(this.nextSibling){
11853                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11854                     return false;
11855                 }
11856             });
11857             return newS;
11858          }
11859         return null;
11860     },
11861
11862     onKeyDown : function(e){
11863         var s = this.selNode || this.lastSelNode;
11864         // undesirable, but required
11865         var sm = this;
11866         if(!s){
11867             return;
11868         }
11869         var k = e.getKey();
11870         switch(k){
11871              case e.DOWN:
11872                  e.stopEvent();
11873                  this.selectNext();
11874              break;
11875              case e.UP:
11876                  e.stopEvent();
11877                  this.selectPrevious();
11878              break;
11879              case e.RIGHT:
11880                  e.preventDefault();
11881                  if(s.hasChildNodes()){
11882                      if(!s.isExpanded()){
11883                          s.expand();
11884                      }else if(s.firstChild){
11885                          this.select(s.firstChild, e);
11886                      }
11887                  }
11888              break;
11889              case e.LEFT:
11890                  e.preventDefault();
11891                  if(s.hasChildNodes() && s.isExpanded()){
11892                      s.collapse();
11893                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11894                      this.select(s.parentNode, e);
11895                  }
11896              break;
11897         };
11898     }
11899 });
11900
11901 /**
11902  * @class Roo.tree.MultiSelectionModel
11903  * @extends Roo.util.Observable
11904  * Multi selection for a TreePanel.
11905  * @param {Object} cfg Configuration
11906  */
11907 Roo.tree.MultiSelectionModel = function(){
11908    this.selNodes = [];
11909    this.selMap = {};
11910    this.addEvents({
11911        /**
11912         * @event selectionchange
11913         * Fires when the selected nodes change
11914         * @param {MultiSelectionModel} this
11915         * @param {Array} nodes Array of the selected nodes
11916         */
11917        "selectionchange" : true
11918    });
11919    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11920    
11921 };
11922
11923 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11924     init : function(tree){
11925         this.tree = tree;
11926         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11927         tree.on("click", this.onNodeClick, this);
11928     },
11929     
11930     onNodeClick : function(node, e){
11931         this.select(node, e, e.ctrlKey);
11932     },
11933     
11934     /**
11935      * Select a node.
11936      * @param {TreeNode} node The node to select
11937      * @param {EventObject} e (optional) An event associated with the selection
11938      * @param {Boolean} keepExisting True to retain existing selections
11939      * @return {TreeNode} The selected node
11940      */
11941     select : function(node, e, keepExisting){
11942         if(keepExisting !== true){
11943             this.clearSelections(true);
11944         }
11945         if(this.isSelected(node)){
11946             this.lastSelNode = node;
11947             return node;
11948         }
11949         this.selNodes.push(node);
11950         this.selMap[node.id] = node;
11951         this.lastSelNode = node;
11952         node.ui.onSelectedChange(true);
11953         this.fireEvent("selectionchange", this, this.selNodes);
11954         return node;
11955     },
11956     
11957     /**
11958      * Deselect a node.
11959      * @param {TreeNode} node The node to unselect
11960      */
11961     unselect : function(node){
11962         if(this.selMap[node.id]){
11963             node.ui.onSelectedChange(false);
11964             var sn = this.selNodes;
11965             var index = -1;
11966             if(sn.indexOf){
11967                 index = sn.indexOf(node);
11968             }else{
11969                 for(var i = 0, len = sn.length; i < len; i++){
11970                     if(sn[i] == node){
11971                         index = i;
11972                         break;
11973                     }
11974                 }
11975             }
11976             if(index != -1){
11977                 this.selNodes.splice(index, 1);
11978             }
11979             delete this.selMap[node.id];
11980             this.fireEvent("selectionchange", this, this.selNodes);
11981         }
11982     },
11983     
11984     /**
11985      * Clear all selections
11986      */
11987     clearSelections : function(suppressEvent){
11988         var sn = this.selNodes;
11989         if(sn.length > 0){
11990             for(var i = 0, len = sn.length; i < len; i++){
11991                 sn[i].ui.onSelectedChange(false);
11992             }
11993             this.selNodes = [];
11994             this.selMap = {};
11995             if(suppressEvent !== true){
11996                 this.fireEvent("selectionchange", this, this.selNodes);
11997             }
11998         }
11999     },
12000     
12001     /**
12002      * Returns true if the node is selected
12003      * @param {TreeNode} node The node to check
12004      * @return {Boolean}
12005      */
12006     isSelected : function(node){
12007         return this.selMap[node.id] ? true : false;  
12008     },
12009     
12010     /**
12011      * Returns an array of the selected nodes
12012      * @return {Array}
12013      */
12014     getSelectedNodes : function(){
12015         return this.selNodes;    
12016     },
12017
12018     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12019
12020     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12021
12022     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12023 });/*
12024  * Based on:
12025  * Ext JS Library 1.1.1
12026  * Copyright(c) 2006-2007, Ext JS, LLC.
12027  *
12028  * Originally Released Under LGPL - original licence link has changed is not relivant.
12029  *
12030  * Fork - LGPL
12031  * <script type="text/javascript">
12032  */
12033  
12034 /**
12035  * @class Roo.tree.TreeNode
12036  * @extends Roo.data.Node
12037  * @cfg {String} text The text for this node
12038  * @cfg {Boolean} expanded true to start the node expanded
12039  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12040  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12041  * @cfg {Boolean} disabled true to start the node disabled
12042  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12043  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12044  * @cfg {String} cls A css class to be added to the node
12045  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12046  * @cfg {String} href URL of the link used for the node (defaults to #)
12047  * @cfg {String} hrefTarget target frame for the link
12048  * @cfg {String} qtip An Ext QuickTip for the node
12049  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12050  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12051  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12052  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12053  * (defaults to undefined with no checkbox rendered)
12054  * @constructor
12055  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12056  */
12057 Roo.tree.TreeNode = function(attributes){
12058     attributes = attributes || {};
12059     if(typeof attributes == "string"){
12060         attributes = {text: attributes};
12061     }
12062     this.childrenRendered = false;
12063     this.rendered = false;
12064     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12065     this.expanded = attributes.expanded === true;
12066     this.isTarget = attributes.isTarget !== false;
12067     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12068     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12069
12070     /**
12071      * Read-only. The text for this node. To change it use setText().
12072      * @type String
12073      */
12074     this.text = attributes.text;
12075     /**
12076      * True if this node is disabled.
12077      * @type Boolean
12078      */
12079     this.disabled = attributes.disabled === true;
12080
12081     this.addEvents({
12082         /**
12083         * @event textchange
12084         * Fires when the text for this node is changed
12085         * @param {Node} this This node
12086         * @param {String} text The new text
12087         * @param {String} oldText The old text
12088         */
12089         "textchange" : true,
12090         /**
12091         * @event beforeexpand
12092         * Fires before this node is expanded, return false to cancel.
12093         * @param {Node} this This node
12094         * @param {Boolean} deep
12095         * @param {Boolean} anim
12096         */
12097         "beforeexpand" : true,
12098         /**
12099         * @event beforecollapse
12100         * Fires before this node is collapsed, return false to cancel.
12101         * @param {Node} this This node
12102         * @param {Boolean} deep
12103         * @param {Boolean} anim
12104         */
12105         "beforecollapse" : true,
12106         /**
12107         * @event expand
12108         * Fires when this node is expanded
12109         * @param {Node} this This node
12110         */
12111         "expand" : true,
12112         /**
12113         * @event disabledchange
12114         * Fires when the disabled status of this node changes
12115         * @param {Node} this This node
12116         * @param {Boolean} disabled
12117         */
12118         "disabledchange" : true,
12119         /**
12120         * @event collapse
12121         * Fires when this node is collapsed
12122         * @param {Node} this This node
12123         */
12124         "collapse" : true,
12125         /**
12126         * @event beforeclick
12127         * Fires before click processing. Return false to cancel the default action.
12128         * @param {Node} this This node
12129         * @param {Roo.EventObject} e The event object
12130         */
12131         "beforeclick":true,
12132         /**
12133         * @event checkchange
12134         * Fires when a node with a checkbox's checked property changes
12135         * @param {Node} this This node
12136         * @param {Boolean} checked
12137         */
12138         "checkchange":true,
12139         /**
12140         * @event click
12141         * Fires when this node is clicked
12142         * @param {Node} this This node
12143         * @param {Roo.EventObject} e The event object
12144         */
12145         "click":true,
12146         /**
12147         * @event dblclick
12148         * Fires when this node is double clicked
12149         * @param {Node} this This node
12150         * @param {Roo.EventObject} e The event object
12151         */
12152         "dblclick":true,
12153         /**
12154         * @event contextmenu
12155         * Fires when this node is right clicked
12156         * @param {Node} this This node
12157         * @param {Roo.EventObject} e The event object
12158         */
12159         "contextmenu":true,
12160         /**
12161         * @event beforechildrenrendered
12162         * Fires right before the child nodes for this node are rendered
12163         * @param {Node} this This node
12164         */
12165         "beforechildrenrendered":true
12166     });
12167
12168     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12169
12170     /**
12171      * Read-only. The UI for this node
12172      * @type TreeNodeUI
12173      */
12174     this.ui = new uiClass(this);
12175     
12176     // finally support items[]
12177     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12178         return;
12179     }
12180     
12181     
12182     Roo.each(this.attributes.items, function(c) {
12183         this.appendChild(Roo.factory(c,Roo.Tree));
12184     }, this);
12185     delete this.attributes.items;
12186     
12187     
12188     
12189 };
12190 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12191     preventHScroll: true,
12192     /**
12193      * Returns true if this node is expanded
12194      * @return {Boolean}
12195      */
12196     isExpanded : function(){
12197         return this.expanded;
12198     },
12199
12200     /**
12201      * Returns the UI object for this node
12202      * @return {TreeNodeUI}
12203      */
12204     getUI : function(){
12205         return this.ui;
12206     },
12207
12208     // private override
12209     setFirstChild : function(node){
12210         var of = this.firstChild;
12211         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12212         if(this.childrenRendered && of && node != of){
12213             of.renderIndent(true, true);
12214         }
12215         if(this.rendered){
12216             this.renderIndent(true, true);
12217         }
12218     },
12219
12220     // private override
12221     setLastChild : function(node){
12222         var ol = this.lastChild;
12223         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12224         if(this.childrenRendered && ol && node != ol){
12225             ol.renderIndent(true, true);
12226         }
12227         if(this.rendered){
12228             this.renderIndent(true, true);
12229         }
12230     },
12231
12232     // these methods are overridden to provide lazy rendering support
12233     // private override
12234     appendChild : function()
12235     {
12236         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12237         if(node && this.childrenRendered){
12238             node.render();
12239         }
12240         this.ui.updateExpandIcon();
12241         return node;
12242     },
12243
12244     // private override
12245     removeChild : function(node){
12246         this.ownerTree.getSelectionModel().unselect(node);
12247         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12248         // if it's been rendered remove dom node
12249         if(this.childrenRendered){
12250             node.ui.remove();
12251         }
12252         if(this.childNodes.length < 1){
12253             this.collapse(false, false);
12254         }else{
12255             this.ui.updateExpandIcon();
12256         }
12257         if(!this.firstChild) {
12258             this.childrenRendered = false;
12259         }
12260         return node;
12261     },
12262
12263     // private override
12264     insertBefore : function(node, refNode){
12265         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12266         if(newNode && refNode && this.childrenRendered){
12267             node.render();
12268         }
12269         this.ui.updateExpandIcon();
12270         return newNode;
12271     },
12272
12273     /**
12274      * Sets the text for this node
12275      * @param {String} text
12276      */
12277     setText : function(text){
12278         var oldText = this.text;
12279         this.text = text;
12280         this.attributes.text = text;
12281         if(this.rendered){ // event without subscribing
12282             this.ui.onTextChange(this, text, oldText);
12283         }
12284         this.fireEvent("textchange", this, text, oldText);
12285     },
12286
12287     /**
12288      * Triggers selection of this node
12289      */
12290     select : function(){
12291         this.getOwnerTree().getSelectionModel().select(this);
12292     },
12293
12294     /**
12295      * Triggers deselection of this node
12296      */
12297     unselect : function(){
12298         this.getOwnerTree().getSelectionModel().unselect(this);
12299     },
12300
12301     /**
12302      * Returns true if this node is selected
12303      * @return {Boolean}
12304      */
12305     isSelected : function(){
12306         return this.getOwnerTree().getSelectionModel().isSelected(this);
12307     },
12308
12309     /**
12310      * Expand this node.
12311      * @param {Boolean} deep (optional) True to expand all children as well
12312      * @param {Boolean} anim (optional) false to cancel the default animation
12313      * @param {Function} callback (optional) A callback to be called when
12314      * expanding this node completes (does not wait for deep expand to complete).
12315      * Called with 1 parameter, this node.
12316      */
12317     expand : function(deep, anim, callback){
12318         if(!this.expanded){
12319             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12320                 return;
12321             }
12322             if(!this.childrenRendered){
12323                 this.renderChildren();
12324             }
12325             this.expanded = true;
12326             
12327             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12328                 this.ui.animExpand(function(){
12329                     this.fireEvent("expand", this);
12330                     if(typeof callback == "function"){
12331                         callback(this);
12332                     }
12333                     if(deep === true){
12334                         this.expandChildNodes(true);
12335                     }
12336                 }.createDelegate(this));
12337                 return;
12338             }else{
12339                 this.ui.expand();
12340                 this.fireEvent("expand", this);
12341                 if(typeof callback == "function"){
12342                     callback(this);
12343                 }
12344             }
12345         }else{
12346            if(typeof callback == "function"){
12347                callback(this);
12348            }
12349         }
12350         if(deep === true){
12351             this.expandChildNodes(true);
12352         }
12353     },
12354
12355     isHiddenRoot : function(){
12356         return this.isRoot && !this.getOwnerTree().rootVisible;
12357     },
12358
12359     /**
12360      * Collapse this node.
12361      * @param {Boolean} deep (optional) True to collapse all children as well
12362      * @param {Boolean} anim (optional) false to cancel the default animation
12363      */
12364     collapse : function(deep, anim){
12365         if(this.expanded && !this.isHiddenRoot()){
12366             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12367                 return;
12368             }
12369             this.expanded = false;
12370             if((this.getOwnerTree().animate && anim !== false) || anim){
12371                 this.ui.animCollapse(function(){
12372                     this.fireEvent("collapse", this);
12373                     if(deep === true){
12374                         this.collapseChildNodes(true);
12375                     }
12376                 }.createDelegate(this));
12377                 return;
12378             }else{
12379                 this.ui.collapse();
12380                 this.fireEvent("collapse", this);
12381             }
12382         }
12383         if(deep === true){
12384             var cs = this.childNodes;
12385             for(var i = 0, len = cs.length; i < len; i++) {
12386                 cs[i].collapse(true, false);
12387             }
12388         }
12389     },
12390
12391     // private
12392     delayedExpand : function(delay){
12393         if(!this.expandProcId){
12394             this.expandProcId = this.expand.defer(delay, this);
12395         }
12396     },
12397
12398     // private
12399     cancelExpand : function(){
12400         if(this.expandProcId){
12401             clearTimeout(this.expandProcId);
12402         }
12403         this.expandProcId = false;
12404     },
12405
12406     /**
12407      * Toggles expanded/collapsed state of the node
12408      */
12409     toggle : function(){
12410         if(this.expanded){
12411             this.collapse();
12412         }else{
12413             this.expand();
12414         }
12415     },
12416
12417     /**
12418      * Ensures all parent nodes are expanded
12419      */
12420     ensureVisible : function(callback){
12421         var tree = this.getOwnerTree();
12422         tree.expandPath(this.parentNode.getPath(), false, function(){
12423             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12424             Roo.callback(callback);
12425         }.createDelegate(this));
12426     },
12427
12428     /**
12429      * Expand all child nodes
12430      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12431      */
12432     expandChildNodes : function(deep){
12433         var cs = this.childNodes;
12434         for(var i = 0, len = cs.length; i < len; i++) {
12435                 cs[i].expand(deep);
12436         }
12437     },
12438
12439     /**
12440      * Collapse all child nodes
12441      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12442      */
12443     collapseChildNodes : function(deep){
12444         var cs = this.childNodes;
12445         for(var i = 0, len = cs.length; i < len; i++) {
12446                 cs[i].collapse(deep);
12447         }
12448     },
12449
12450     /**
12451      * Disables this node
12452      */
12453     disable : function(){
12454         this.disabled = true;
12455         this.unselect();
12456         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12457             this.ui.onDisableChange(this, true);
12458         }
12459         this.fireEvent("disabledchange", this, true);
12460     },
12461
12462     /**
12463      * Enables this node
12464      */
12465     enable : function(){
12466         this.disabled = false;
12467         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12468             this.ui.onDisableChange(this, false);
12469         }
12470         this.fireEvent("disabledchange", this, false);
12471     },
12472
12473     // private
12474     renderChildren : function(suppressEvent){
12475         if(suppressEvent !== false){
12476             this.fireEvent("beforechildrenrendered", this);
12477         }
12478         var cs = this.childNodes;
12479         for(var i = 0, len = cs.length; i < len; i++){
12480             cs[i].render(true);
12481         }
12482         this.childrenRendered = true;
12483     },
12484
12485     // private
12486     sort : function(fn, scope){
12487         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12488         if(this.childrenRendered){
12489             var cs = this.childNodes;
12490             for(var i = 0, len = cs.length; i < len; i++){
12491                 cs[i].render(true);
12492             }
12493         }
12494     },
12495
12496     // private
12497     render : function(bulkRender){
12498         this.ui.render(bulkRender);
12499         if(!this.rendered){
12500             this.rendered = true;
12501             if(this.expanded){
12502                 this.expanded = false;
12503                 this.expand(false, false);
12504             }
12505         }
12506     },
12507
12508     // private
12509     renderIndent : function(deep, refresh){
12510         if(refresh){
12511             this.ui.childIndent = null;
12512         }
12513         this.ui.renderIndent();
12514         if(deep === true && this.childrenRendered){
12515             var cs = this.childNodes;
12516             for(var i = 0, len = cs.length; i < len; i++){
12517                 cs[i].renderIndent(true, refresh);
12518             }
12519         }
12520     }
12521 });/*
12522  * Based on:
12523  * Ext JS Library 1.1.1
12524  * Copyright(c) 2006-2007, Ext JS, LLC.
12525  *
12526  * Originally Released Under LGPL - original licence link has changed is not relivant.
12527  *
12528  * Fork - LGPL
12529  * <script type="text/javascript">
12530  */
12531  
12532 /**
12533  * @class Roo.tree.AsyncTreeNode
12534  * @extends Roo.tree.TreeNode
12535  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12536  * @constructor
12537  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12538  */
12539  Roo.tree.AsyncTreeNode = function(config){
12540     this.loaded = false;
12541     this.loading = false;
12542     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12543     /**
12544     * @event beforeload
12545     * Fires before this node is loaded, return false to cancel
12546     * @param {Node} this This node
12547     */
12548     this.addEvents({'beforeload':true, 'load': true});
12549     /**
12550     * @event load
12551     * Fires when this node is loaded
12552     * @param {Node} this This node
12553     */
12554     /**
12555      * The loader used by this node (defaults to using the tree's defined loader)
12556      * @type TreeLoader
12557      * @property loader
12558      */
12559 };
12560 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12561     expand : function(deep, anim, callback){
12562         if(this.loading){ // if an async load is already running, waiting til it's done
12563             var timer;
12564             var f = function(){
12565                 if(!this.loading){ // done loading
12566                     clearInterval(timer);
12567                     this.expand(deep, anim, callback);
12568                 }
12569             }.createDelegate(this);
12570             timer = setInterval(f, 200);
12571             return;
12572         }
12573         if(!this.loaded){
12574             if(this.fireEvent("beforeload", this) === false){
12575                 return;
12576             }
12577             this.loading = true;
12578             this.ui.beforeLoad(this);
12579             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12580             if(loader){
12581                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12582                 return;
12583             }
12584         }
12585         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12586     },
12587     
12588     /**
12589      * Returns true if this node is currently loading
12590      * @return {Boolean}
12591      */
12592     isLoading : function(){
12593         return this.loading;  
12594     },
12595     
12596     loadComplete : function(deep, anim, callback){
12597         this.loading = false;
12598         this.loaded = true;
12599         this.ui.afterLoad(this);
12600         this.fireEvent("load", this);
12601         this.expand(deep, anim, callback);
12602     },
12603     
12604     /**
12605      * Returns true if this node has been loaded
12606      * @return {Boolean}
12607      */
12608     isLoaded : function(){
12609         return this.loaded;
12610     },
12611     
12612     hasChildNodes : function(){
12613         if(!this.isLeaf() && !this.loaded){
12614             return true;
12615         }else{
12616             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12617         }
12618     },
12619
12620     /**
12621      * Trigger a reload for this node
12622      * @param {Function} callback
12623      */
12624     reload : function(callback){
12625         this.collapse(false, false);
12626         while(this.firstChild){
12627             this.removeChild(this.firstChild);
12628         }
12629         this.childrenRendered = false;
12630         this.loaded = false;
12631         if(this.isHiddenRoot()){
12632             this.expanded = false;
12633         }
12634         this.expand(false, false, callback);
12635     }
12636 });/*
12637  * Based on:
12638  * Ext JS Library 1.1.1
12639  * Copyright(c) 2006-2007, Ext JS, LLC.
12640  *
12641  * Originally Released Under LGPL - original licence link has changed is not relivant.
12642  *
12643  * Fork - LGPL
12644  * <script type="text/javascript">
12645  */
12646  
12647 /**
12648  * @class Roo.tree.TreeNodeUI
12649  * @constructor
12650  * @param {Object} node The node to render
12651  * The TreeNode UI implementation is separate from the
12652  * tree implementation. Unless you are customizing the tree UI,
12653  * you should never have to use this directly.
12654  */
12655 Roo.tree.TreeNodeUI = function(node){
12656     this.node = node;
12657     this.rendered = false;
12658     this.animating = false;
12659     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12660 };
12661
12662 Roo.tree.TreeNodeUI.prototype = {
12663     removeChild : function(node){
12664         if(this.rendered){
12665             this.ctNode.removeChild(node.ui.getEl());
12666         }
12667     },
12668
12669     beforeLoad : function(){
12670          this.addClass("x-tree-node-loading");
12671     },
12672
12673     afterLoad : function(){
12674          this.removeClass("x-tree-node-loading");
12675     },
12676
12677     onTextChange : function(node, text, oldText){
12678         if(this.rendered){
12679             this.textNode.innerHTML = text;
12680         }
12681     },
12682
12683     onDisableChange : function(node, state){
12684         this.disabled = state;
12685         if(state){
12686             this.addClass("x-tree-node-disabled");
12687         }else{
12688             this.removeClass("x-tree-node-disabled");
12689         }
12690     },
12691
12692     onSelectedChange : function(state){
12693         if(state){
12694             this.focus();
12695             this.addClass("x-tree-selected");
12696         }else{
12697             //this.blur();
12698             this.removeClass("x-tree-selected");
12699         }
12700     },
12701
12702     onMove : function(tree, node, oldParent, newParent, index, refNode){
12703         this.childIndent = null;
12704         if(this.rendered){
12705             var targetNode = newParent.ui.getContainer();
12706             if(!targetNode){//target not rendered
12707                 this.holder = document.createElement("div");
12708                 this.holder.appendChild(this.wrap);
12709                 return;
12710             }
12711             var insertBefore = refNode ? refNode.ui.getEl() : null;
12712             if(insertBefore){
12713                 targetNode.insertBefore(this.wrap, insertBefore);
12714             }else{
12715                 targetNode.appendChild(this.wrap);
12716             }
12717             this.node.renderIndent(true);
12718         }
12719     },
12720
12721     addClass : function(cls){
12722         if(this.elNode){
12723             Roo.fly(this.elNode).addClass(cls);
12724         }
12725     },
12726
12727     removeClass : function(cls){
12728         if(this.elNode){
12729             Roo.fly(this.elNode).removeClass(cls);
12730         }
12731     },
12732
12733     remove : function(){
12734         if(this.rendered){
12735             this.holder = document.createElement("div");
12736             this.holder.appendChild(this.wrap);
12737         }
12738     },
12739
12740     fireEvent : function(){
12741         return this.node.fireEvent.apply(this.node, arguments);
12742     },
12743
12744     initEvents : function(){
12745         this.node.on("move", this.onMove, this);
12746         var E = Roo.EventManager;
12747         var a = this.anchor;
12748
12749         var el = Roo.fly(a, '_treeui');
12750
12751         if(Roo.isOpera){ // opera render bug ignores the CSS
12752             el.setStyle("text-decoration", "none");
12753         }
12754
12755         el.on("click", this.onClick, this);
12756         el.on("dblclick", this.onDblClick, this);
12757
12758         if(this.checkbox){
12759             Roo.EventManager.on(this.checkbox,
12760                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12761         }
12762
12763         el.on("contextmenu", this.onContextMenu, this);
12764
12765         var icon = Roo.fly(this.iconNode);
12766         icon.on("click", this.onClick, this);
12767         icon.on("dblclick", this.onDblClick, this);
12768         icon.on("contextmenu", this.onContextMenu, this);
12769         E.on(this.ecNode, "click", this.ecClick, this, true);
12770
12771         if(this.node.disabled){
12772             this.addClass("x-tree-node-disabled");
12773         }
12774         if(this.node.hidden){
12775             this.addClass("x-tree-node-disabled");
12776         }
12777         var ot = this.node.getOwnerTree();
12778         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12779         if(dd && (!this.node.isRoot || ot.rootVisible)){
12780             Roo.dd.Registry.register(this.elNode, {
12781                 node: this.node,
12782                 handles: this.getDDHandles(),
12783                 isHandle: false
12784             });
12785         }
12786     },
12787
12788     getDDHandles : function(){
12789         return [this.iconNode, this.textNode];
12790     },
12791
12792     hide : function(){
12793         if(this.rendered){
12794             this.wrap.style.display = "none";
12795         }
12796     },
12797
12798     show : function(){
12799         if(this.rendered){
12800             this.wrap.style.display = "";
12801         }
12802     },
12803
12804     onContextMenu : function(e){
12805         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12806             e.preventDefault();
12807             this.focus();
12808             this.fireEvent("contextmenu", this.node, e);
12809         }
12810     },
12811
12812     onClick : function(e){
12813         if(this.dropping){
12814             e.stopEvent();
12815             return;
12816         }
12817         if(this.fireEvent("beforeclick", this.node, e) !== false){
12818             if(!this.disabled && this.node.attributes.href){
12819                 this.fireEvent("click", this.node, e);
12820                 return;
12821             }
12822             e.preventDefault();
12823             if(this.disabled){
12824                 return;
12825             }
12826
12827             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12828                 this.node.toggle();
12829             }
12830
12831             this.fireEvent("click", this.node, e);
12832         }else{
12833             e.stopEvent();
12834         }
12835     },
12836
12837     onDblClick : function(e){
12838         e.preventDefault();
12839         if(this.disabled){
12840             return;
12841         }
12842         if(this.checkbox){
12843             this.toggleCheck();
12844         }
12845         if(!this.animating && this.node.hasChildNodes()){
12846             this.node.toggle();
12847         }
12848         this.fireEvent("dblclick", this.node, e);
12849     },
12850
12851     onCheckChange : function(){
12852         var checked = this.checkbox.checked;
12853         this.node.attributes.checked = checked;
12854         this.fireEvent('checkchange', this.node, checked);
12855     },
12856
12857     ecClick : function(e){
12858         if(!this.animating && this.node.hasChildNodes()){
12859             this.node.toggle();
12860         }
12861     },
12862
12863     startDrop : function(){
12864         this.dropping = true;
12865     },
12866
12867     // delayed drop so the click event doesn't get fired on a drop
12868     endDrop : function(){
12869        setTimeout(function(){
12870            this.dropping = false;
12871        }.createDelegate(this), 50);
12872     },
12873
12874     expand : function(){
12875         this.updateExpandIcon();
12876         this.ctNode.style.display = "";
12877     },
12878
12879     focus : function(){
12880         if(!this.node.preventHScroll){
12881             try{this.anchor.focus();
12882             }catch(e){}
12883         }else if(!Roo.isIE){
12884             try{
12885                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12886                 var l = noscroll.scrollLeft;
12887                 this.anchor.focus();
12888                 noscroll.scrollLeft = l;
12889             }catch(e){}
12890         }
12891     },
12892
12893     toggleCheck : function(value){
12894         var cb = this.checkbox;
12895         if(cb){
12896             cb.checked = (value === undefined ? !cb.checked : value);
12897         }
12898     },
12899
12900     blur : function(){
12901         try{
12902             this.anchor.blur();
12903         }catch(e){}
12904     },
12905
12906     animExpand : function(callback){
12907         var ct = Roo.get(this.ctNode);
12908         ct.stopFx();
12909         if(!this.node.hasChildNodes()){
12910             this.updateExpandIcon();
12911             this.ctNode.style.display = "";
12912             Roo.callback(callback);
12913             return;
12914         }
12915         this.animating = true;
12916         this.updateExpandIcon();
12917
12918         ct.slideIn('t', {
12919            callback : function(){
12920                this.animating = false;
12921                Roo.callback(callback);
12922             },
12923             scope: this,
12924             duration: this.node.ownerTree.duration || .25
12925         });
12926     },
12927
12928     highlight : function(){
12929         var tree = this.node.getOwnerTree();
12930         Roo.fly(this.wrap).highlight(
12931             tree.hlColor || "C3DAF9",
12932             {endColor: tree.hlBaseColor}
12933         );
12934     },
12935
12936     collapse : function(){
12937         this.updateExpandIcon();
12938         this.ctNode.style.display = "none";
12939     },
12940
12941     animCollapse : function(callback){
12942         var ct = Roo.get(this.ctNode);
12943         ct.enableDisplayMode('block');
12944         ct.stopFx();
12945
12946         this.animating = true;
12947         this.updateExpandIcon();
12948
12949         ct.slideOut('t', {
12950             callback : function(){
12951                this.animating = false;
12952                Roo.callback(callback);
12953             },
12954             scope: this,
12955             duration: this.node.ownerTree.duration || .25
12956         });
12957     },
12958
12959     getContainer : function(){
12960         return this.ctNode;
12961     },
12962
12963     getEl : function(){
12964         return this.wrap;
12965     },
12966
12967     appendDDGhost : function(ghostNode){
12968         ghostNode.appendChild(this.elNode.cloneNode(true));
12969     },
12970
12971     getDDRepairXY : function(){
12972         return Roo.lib.Dom.getXY(this.iconNode);
12973     },
12974
12975     onRender : function(){
12976         this.render();
12977     },
12978
12979     render : function(bulkRender){
12980         var n = this.node, a = n.attributes;
12981         var targetNode = n.parentNode ?
12982               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
12983
12984         if(!this.rendered){
12985             this.rendered = true;
12986
12987             this.renderElements(n, a, targetNode, bulkRender);
12988
12989             if(a.qtip){
12990                if(this.textNode.setAttributeNS){
12991                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
12992                    if(a.qtipTitle){
12993                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
12994                    }
12995                }else{
12996                    this.textNode.setAttribute("ext:qtip", a.qtip);
12997                    if(a.qtipTitle){
12998                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
12999                    }
13000                }
13001             }else if(a.qtipCfg){
13002                 a.qtipCfg.target = Roo.id(this.textNode);
13003                 Roo.QuickTips.register(a.qtipCfg);
13004             }
13005             this.initEvents();
13006             if(!this.node.expanded){
13007                 this.updateExpandIcon();
13008             }
13009         }else{
13010             if(bulkRender === true) {
13011                 targetNode.appendChild(this.wrap);
13012             }
13013         }
13014     },
13015
13016     renderElements : function(n, a, targetNode, bulkRender)
13017     {
13018         // add some indent caching, this helps performance when rendering a large tree
13019         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13020         var t = n.getOwnerTree();
13021         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13022         if (typeof(n.attributes.html) != 'undefined') {
13023             txt = n.attributes.html;
13024         }
13025         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13026         var cb = typeof a.checked == 'boolean';
13027         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13028         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13029             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13030             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13031             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13032             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13033             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13034              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13035                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13036             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13037             "</li>"];
13038
13039         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13040             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13041                                 n.nextSibling.ui.getEl(), buf.join(""));
13042         }else{
13043             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13044         }
13045
13046         this.elNode = this.wrap.childNodes[0];
13047         this.ctNode = this.wrap.childNodes[1];
13048         var cs = this.elNode.childNodes;
13049         this.indentNode = cs[0];
13050         this.ecNode = cs[1];
13051         this.iconNode = cs[2];
13052         var index = 3;
13053         if(cb){
13054             this.checkbox = cs[3];
13055             index++;
13056         }
13057         this.anchor = cs[index];
13058         this.textNode = cs[index].firstChild;
13059     },
13060
13061     getAnchor : function(){
13062         return this.anchor;
13063     },
13064
13065     getTextEl : function(){
13066         return this.textNode;
13067     },
13068
13069     getIconEl : function(){
13070         return this.iconNode;
13071     },
13072
13073     isChecked : function(){
13074         return this.checkbox ? this.checkbox.checked : false;
13075     },
13076
13077     updateExpandIcon : function(){
13078         if(this.rendered){
13079             var n = this.node, c1, c2;
13080             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13081             var hasChild = n.hasChildNodes();
13082             if(hasChild){
13083                 if(n.expanded){
13084                     cls += "-minus";
13085                     c1 = "x-tree-node-collapsed";
13086                     c2 = "x-tree-node-expanded";
13087                 }else{
13088                     cls += "-plus";
13089                     c1 = "x-tree-node-expanded";
13090                     c2 = "x-tree-node-collapsed";
13091                 }
13092                 if(this.wasLeaf){
13093                     this.removeClass("x-tree-node-leaf");
13094                     this.wasLeaf = false;
13095                 }
13096                 if(this.c1 != c1 || this.c2 != c2){
13097                     Roo.fly(this.elNode).replaceClass(c1, c2);
13098                     this.c1 = c1; this.c2 = c2;
13099                 }
13100             }else{
13101                 // this changes non-leafs into leafs if they have no children.
13102                 // it's not very rational behaviour..
13103                 
13104                 if(!this.wasLeaf && this.node.leaf){
13105                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13106                     delete this.c1;
13107                     delete this.c2;
13108                     this.wasLeaf = true;
13109                 }
13110             }
13111             var ecc = "x-tree-ec-icon "+cls;
13112             if(this.ecc != ecc){
13113                 this.ecNode.className = ecc;
13114                 this.ecc = ecc;
13115             }
13116         }
13117     },
13118
13119     getChildIndent : function(){
13120         if(!this.childIndent){
13121             var buf = [];
13122             var p = this.node;
13123             while(p){
13124                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13125                     if(!p.isLast()) {
13126                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13127                     } else {
13128                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13129                     }
13130                 }
13131                 p = p.parentNode;
13132             }
13133             this.childIndent = buf.join("");
13134         }
13135         return this.childIndent;
13136     },
13137
13138     renderIndent : function(){
13139         if(this.rendered){
13140             var indent = "";
13141             var p = this.node.parentNode;
13142             if(p){
13143                 indent = p.ui.getChildIndent();
13144             }
13145             if(this.indentMarkup != indent){ // don't rerender if not required
13146                 this.indentNode.innerHTML = indent;
13147                 this.indentMarkup = indent;
13148             }
13149             this.updateExpandIcon();
13150         }
13151     }
13152 };
13153
13154 Roo.tree.RootTreeNodeUI = function(){
13155     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13156 };
13157 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13158     render : function(){
13159         if(!this.rendered){
13160             var targetNode = this.node.ownerTree.innerCt.dom;
13161             this.node.expanded = true;
13162             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13163             this.wrap = this.ctNode = targetNode.firstChild;
13164         }
13165     },
13166     collapse : function(){
13167     },
13168     expand : function(){
13169     }
13170 });/*
13171  * Based on:
13172  * Ext JS Library 1.1.1
13173  * Copyright(c) 2006-2007, Ext JS, LLC.
13174  *
13175  * Originally Released Under LGPL - original licence link has changed is not relivant.
13176  *
13177  * Fork - LGPL
13178  * <script type="text/javascript">
13179  */
13180 /**
13181  * @class Roo.tree.TreeLoader
13182  * @extends Roo.util.Observable
13183  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13184  * nodes from a specified URL. The response must be a javascript Array definition
13185  * who's elements are node definition objects. eg:
13186  * <pre><code>
13187 {  success : true,
13188    data :      [
13189    
13190     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13191     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13192     ]
13193 }
13194
13195
13196 </code></pre>
13197  * <br><br>
13198  * The old style respose with just an array is still supported, but not recommended.
13199  * <br><br>
13200  *
13201  * A server request is sent, and child nodes are loaded only when a node is expanded.
13202  * The loading node's id is passed to the server under the parameter name "node" to
13203  * enable the server to produce the correct child nodes.
13204  * <br><br>
13205  * To pass extra parameters, an event handler may be attached to the "beforeload"
13206  * event, and the parameters specified in the TreeLoader's baseParams property:
13207  * <pre><code>
13208     myTreeLoader.on("beforeload", function(treeLoader, node) {
13209         this.baseParams.category = node.attributes.category;
13210     }, this);
13211     
13212 </code></pre>
13213  *
13214  * This would pass an HTTP parameter called "category" to the server containing
13215  * the value of the Node's "category" attribute.
13216  * @constructor
13217  * Creates a new Treeloader.
13218  * @param {Object} config A config object containing config properties.
13219  */
13220 Roo.tree.TreeLoader = function(config){
13221     this.baseParams = {};
13222     this.requestMethod = "POST";
13223     Roo.apply(this, config);
13224
13225     this.addEvents({
13226     
13227         /**
13228          * @event beforeload
13229          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13230          * @param {Object} This TreeLoader object.
13231          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13232          * @param {Object} callback The callback function specified in the {@link #load} call.
13233          */
13234         beforeload : true,
13235         /**
13236          * @event load
13237          * Fires when the node has been successfuly loaded.
13238          * @param {Object} This TreeLoader object.
13239          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13240          * @param {Object} response The response object containing the data from the server.
13241          */
13242         load : true,
13243         /**
13244          * @event loadexception
13245          * Fires if the network request failed.
13246          * @param {Object} This TreeLoader object.
13247          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13248          * @param {Object} response The response object containing the data from the server.
13249          */
13250         loadexception : true,
13251         /**
13252          * @event create
13253          * Fires before a node is created, enabling you to return custom Node types 
13254          * @param {Object} This TreeLoader object.
13255          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13256          */
13257         create : true
13258     });
13259
13260     Roo.tree.TreeLoader.superclass.constructor.call(this);
13261 };
13262
13263 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13264     /**
13265     * @cfg {String} dataUrl The URL from which to request a Json string which
13266     * specifies an array of node definition object representing the child nodes
13267     * to be loaded.
13268     */
13269     /**
13270     * @cfg {String} requestMethod either GET or POST
13271     * defaults to POST (due to BC)
13272     * to be loaded.
13273     */
13274     /**
13275     * @cfg {Object} baseParams (optional) An object containing properties which
13276     * specify HTTP parameters to be passed to each request for child nodes.
13277     */
13278     /**
13279     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13280     * created by this loader. If the attributes sent by the server have an attribute in this object,
13281     * they take priority.
13282     */
13283     /**
13284     * @cfg {Object} uiProviders (optional) An object containing properties which
13285     * 
13286     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13287     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13288     * <i>uiProvider</i> attribute of a returned child node is a string rather
13289     * than a reference to a TreeNodeUI implementation, this that string value
13290     * is used as a property name in the uiProviders object. You can define the provider named
13291     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13292     */
13293     uiProviders : {},
13294
13295     /**
13296     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13297     * child nodes before loading.
13298     */
13299     clearOnLoad : true,
13300
13301     /**
13302     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13303     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13304     * Grid query { data : [ .....] }
13305     */
13306     
13307     root : false,
13308      /**
13309     * @cfg {String} queryParam (optional) 
13310     * Name of the query as it will be passed on the querystring (defaults to 'node')
13311     * eg. the request will be ?node=[id]
13312     */
13313     
13314     
13315     queryParam: false,
13316     
13317     /**
13318      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13319      * This is called automatically when a node is expanded, but may be used to reload
13320      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13321      * @param {Roo.tree.TreeNode} node
13322      * @param {Function} callback
13323      */
13324     load : function(node, callback){
13325         if(this.clearOnLoad){
13326             while(node.firstChild){
13327                 node.removeChild(node.firstChild);
13328             }
13329         }
13330         if(node.attributes.children){ // preloaded json children
13331             var cs = node.attributes.children;
13332             for(var i = 0, len = cs.length; i < len; i++){
13333                 node.appendChild(this.createNode(cs[i]));
13334             }
13335             if(typeof callback == "function"){
13336                 callback();
13337             }
13338         }else if(this.dataUrl){
13339             this.requestData(node, callback);
13340         }
13341     },
13342
13343     getParams: function(node){
13344         var buf = [], bp = this.baseParams;
13345         for(var key in bp){
13346             if(typeof bp[key] != "function"){
13347                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13348             }
13349         }
13350         var n = this.queryParam === false ? 'node' : this.queryParam;
13351         buf.push(n + "=", encodeURIComponent(node.id));
13352         return buf.join("");
13353     },
13354
13355     requestData : function(node, callback){
13356         if(this.fireEvent("beforeload", this, node, callback) !== false){
13357             this.transId = Roo.Ajax.request({
13358                 method:this.requestMethod,
13359                 url: this.dataUrl||this.url,
13360                 success: this.handleResponse,
13361                 failure: this.handleFailure,
13362                 scope: this,
13363                 argument: {callback: callback, node: node},
13364                 params: this.getParams(node)
13365             });
13366         }else{
13367             // if the load is cancelled, make sure we notify
13368             // the node that we are done
13369             if(typeof callback == "function"){
13370                 callback();
13371             }
13372         }
13373     },
13374
13375     isLoading : function(){
13376         return this.transId ? true : false;
13377     },
13378
13379     abort : function(){
13380         if(this.isLoading()){
13381             Roo.Ajax.abort(this.transId);
13382         }
13383     },
13384
13385     // private
13386     createNode : function(attr)
13387     {
13388         // apply baseAttrs, nice idea Corey!
13389         if(this.baseAttrs){
13390             Roo.applyIf(attr, this.baseAttrs);
13391         }
13392         if(this.applyLoader !== false){
13393             attr.loader = this;
13394         }
13395         // uiProvider = depreciated..
13396         
13397         if(typeof(attr.uiProvider) == 'string'){
13398            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13399                 /**  eval:var:attr */ eval(attr.uiProvider);
13400         }
13401         if(typeof(this.uiProviders['default']) != 'undefined') {
13402             attr.uiProvider = this.uiProviders['default'];
13403         }
13404         
13405         this.fireEvent('create', this, attr);
13406         
13407         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13408         return(attr.leaf ?
13409                         new Roo.tree.TreeNode(attr) :
13410                         new Roo.tree.AsyncTreeNode(attr));
13411     },
13412
13413     processResponse : function(response, node, callback)
13414     {
13415         var json = response.responseText;
13416         try {
13417             
13418             var o = Roo.decode(json);
13419             
13420             if (this.root === false && typeof(o.success) != undefined) {
13421                 this.root = 'data'; // the default behaviour for list like data..
13422                 }
13423                 
13424             if (this.root !== false &&  !o.success) {
13425                 // it's a failure condition.
13426                 var a = response.argument;
13427                 this.fireEvent("loadexception", this, a.node, response);
13428                 Roo.log("Load failed - should have a handler really");
13429                 return;
13430             }
13431             
13432             
13433             
13434             if (this.root !== false) {
13435                  o = o[this.root];
13436             }
13437             
13438             for(var i = 0, len = o.length; i < len; i++){
13439                 var n = this.createNode(o[i]);
13440                 if(n){
13441                     node.appendChild(n);
13442                 }
13443             }
13444             if(typeof callback == "function"){
13445                 callback(this, node);
13446             }
13447         }catch(e){
13448             this.handleFailure(response);
13449         }
13450     },
13451
13452     handleResponse : function(response){
13453         this.transId = false;
13454         var a = response.argument;
13455         this.processResponse(response, a.node, a.callback);
13456         this.fireEvent("load", this, a.node, response);
13457     },
13458
13459     handleFailure : function(response)
13460     {
13461         // should handle failure better..
13462         this.transId = false;
13463         var a = response.argument;
13464         this.fireEvent("loadexception", this, a.node, response);
13465         if(typeof a.callback == "function"){
13466             a.callback(this, a.node);
13467         }
13468     }
13469 });/*
13470  * Based on:
13471  * Ext JS Library 1.1.1
13472  * Copyright(c) 2006-2007, Ext JS, LLC.
13473  *
13474  * Originally Released Under LGPL - original licence link has changed is not relivant.
13475  *
13476  * Fork - LGPL
13477  * <script type="text/javascript">
13478  */
13479
13480 /**
13481 * @class Roo.tree.TreeFilter
13482 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13483 * @param {TreePanel} tree
13484 * @param {Object} config (optional)
13485  */
13486 Roo.tree.TreeFilter = function(tree, config){
13487     this.tree = tree;
13488     this.filtered = {};
13489     Roo.apply(this, config);
13490 };
13491
13492 Roo.tree.TreeFilter.prototype = {
13493     clearBlank:false,
13494     reverse:false,
13495     autoClear:false,
13496     remove:false,
13497
13498      /**
13499      * Filter the data by a specific attribute.
13500      * @param {String/RegExp} value Either string that the attribute value
13501      * should start with or a RegExp to test against the attribute
13502      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13503      * @param {TreeNode} startNode (optional) The node to start the filter at.
13504      */
13505     filter : function(value, attr, startNode){
13506         attr = attr || "text";
13507         var f;
13508         if(typeof value == "string"){
13509             var vlen = value.length;
13510             // auto clear empty filter
13511             if(vlen == 0 && this.clearBlank){
13512                 this.clear();
13513                 return;
13514             }
13515             value = value.toLowerCase();
13516             f = function(n){
13517                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13518             };
13519         }else if(value.exec){ // regex?
13520             f = function(n){
13521                 return value.test(n.attributes[attr]);
13522             };
13523         }else{
13524             throw 'Illegal filter type, must be string or regex';
13525         }
13526         this.filterBy(f, null, startNode);
13527         },
13528
13529     /**
13530      * Filter by a function. The passed function will be called with each
13531      * node in the tree (or from the startNode). If the function returns true, the node is kept
13532      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13533      * @param {Function} fn The filter function
13534      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13535      */
13536     filterBy : function(fn, scope, startNode){
13537         startNode = startNode || this.tree.root;
13538         if(this.autoClear){
13539             this.clear();
13540         }
13541         var af = this.filtered, rv = this.reverse;
13542         var f = function(n){
13543             if(n == startNode){
13544                 return true;
13545             }
13546             if(af[n.id]){
13547                 return false;
13548             }
13549             var m = fn.call(scope || n, n);
13550             if(!m || rv){
13551                 af[n.id] = n;
13552                 n.ui.hide();
13553                 return false;
13554             }
13555             return true;
13556         };
13557         startNode.cascade(f);
13558         if(this.remove){
13559            for(var id in af){
13560                if(typeof id != "function"){
13561                    var n = af[id];
13562                    if(n && n.parentNode){
13563                        n.parentNode.removeChild(n);
13564                    }
13565                }
13566            }
13567         }
13568     },
13569
13570     /**
13571      * Clears the current filter. Note: with the "remove" option
13572      * set a filter cannot be cleared.
13573      */
13574     clear : function(){
13575         var t = this.tree;
13576         var af = this.filtered;
13577         for(var id in af){
13578             if(typeof id != "function"){
13579                 var n = af[id];
13580                 if(n){
13581                     n.ui.show();
13582                 }
13583             }
13584         }
13585         this.filtered = {};
13586     }
13587 };
13588 /*
13589  * Based on:
13590  * Ext JS Library 1.1.1
13591  * Copyright(c) 2006-2007, Ext JS, LLC.
13592  *
13593  * Originally Released Under LGPL - original licence link has changed is not relivant.
13594  *
13595  * Fork - LGPL
13596  * <script type="text/javascript">
13597  */
13598  
13599
13600 /**
13601  * @class Roo.tree.TreeSorter
13602  * Provides sorting of nodes in a TreePanel
13603  * 
13604  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13605  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13606  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13607  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13608  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13609  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13610  * @constructor
13611  * @param {TreePanel} tree
13612  * @param {Object} config
13613  */
13614 Roo.tree.TreeSorter = function(tree, config){
13615     Roo.apply(this, config);
13616     tree.on("beforechildrenrendered", this.doSort, this);
13617     tree.on("append", this.updateSort, this);
13618     tree.on("insert", this.updateSort, this);
13619     
13620     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13621     var p = this.property || "text";
13622     var sortType = this.sortType;
13623     var fs = this.folderSort;
13624     var cs = this.caseSensitive === true;
13625     var leafAttr = this.leafAttr || 'leaf';
13626
13627     this.sortFn = function(n1, n2){
13628         if(fs){
13629             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13630                 return 1;
13631             }
13632             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13633                 return -1;
13634             }
13635         }
13636         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13637         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13638         if(v1 < v2){
13639                         return dsc ? +1 : -1;
13640                 }else if(v1 > v2){
13641                         return dsc ? -1 : +1;
13642         }else{
13643                 return 0;
13644         }
13645     };
13646 };
13647
13648 Roo.tree.TreeSorter.prototype = {
13649     doSort : function(node){
13650         node.sort(this.sortFn);
13651     },
13652     
13653     compareNodes : function(n1, n2){
13654         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13655     },
13656     
13657     updateSort : function(tree, node){
13658         if(node.childrenRendered){
13659             this.doSort.defer(1, this, [node]);
13660         }
13661     }
13662 };/*
13663  * Based on:
13664  * Ext JS Library 1.1.1
13665  * Copyright(c) 2006-2007, Ext JS, LLC.
13666  *
13667  * Originally Released Under LGPL - original licence link has changed is not relivant.
13668  *
13669  * Fork - LGPL
13670  * <script type="text/javascript">
13671  */
13672
13673 if(Roo.dd.DropZone){
13674     
13675 Roo.tree.TreeDropZone = function(tree, config){
13676     this.allowParentInsert = false;
13677     this.allowContainerDrop = false;
13678     this.appendOnly = false;
13679     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13680     this.tree = tree;
13681     this.lastInsertClass = "x-tree-no-status";
13682     this.dragOverData = {};
13683 };
13684
13685 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13686     ddGroup : "TreeDD",
13687     scroll:  true,
13688     
13689     expandDelay : 1000,
13690     
13691     expandNode : function(node){
13692         if(node.hasChildNodes() && !node.isExpanded()){
13693             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13694         }
13695     },
13696     
13697     queueExpand : function(node){
13698         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13699     },
13700     
13701     cancelExpand : function(){
13702         if(this.expandProcId){
13703             clearTimeout(this.expandProcId);
13704             this.expandProcId = false;
13705         }
13706     },
13707     
13708     isValidDropPoint : function(n, pt, dd, e, data){
13709         if(!n || !data){ return false; }
13710         var targetNode = n.node;
13711         var dropNode = data.node;
13712         // default drop rules
13713         if(!(targetNode && targetNode.isTarget && pt)){
13714             return false;
13715         }
13716         if(pt == "append" && targetNode.allowChildren === false){
13717             return false;
13718         }
13719         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13720             return false;
13721         }
13722         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13723             return false;
13724         }
13725         // reuse the object
13726         var overEvent = this.dragOverData;
13727         overEvent.tree = this.tree;
13728         overEvent.target = targetNode;
13729         overEvent.data = data;
13730         overEvent.point = pt;
13731         overEvent.source = dd;
13732         overEvent.rawEvent = e;
13733         overEvent.dropNode = dropNode;
13734         overEvent.cancel = false;  
13735         var result = this.tree.fireEvent("nodedragover", overEvent);
13736         return overEvent.cancel === false && result !== false;
13737     },
13738     
13739     getDropPoint : function(e, n, dd)
13740     {
13741         var tn = n.node;
13742         if(tn.isRoot){
13743             return tn.allowChildren !== false ? "append" : false; // always append for root
13744         }
13745         var dragEl = n.ddel;
13746         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13747         var y = Roo.lib.Event.getPageY(e);
13748         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13749         
13750         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13751         var noAppend = tn.allowChildren === false;
13752         if(this.appendOnly || tn.parentNode.allowChildren === false){
13753             return noAppend ? false : "append";
13754         }
13755         var noBelow = false;
13756         if(!this.allowParentInsert){
13757             noBelow = tn.hasChildNodes() && tn.isExpanded();
13758         }
13759         var q = (b - t) / (noAppend ? 2 : 3);
13760         if(y >= t && y < (t + q)){
13761             return "above";
13762         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13763             return "below";
13764         }else{
13765             return "append";
13766         }
13767     },
13768     
13769     onNodeEnter : function(n, dd, e, data)
13770     {
13771         this.cancelExpand();
13772     },
13773     
13774     onNodeOver : function(n, dd, e, data)
13775     {
13776        
13777         var pt = this.getDropPoint(e, n, dd);
13778         var node = n.node;
13779         
13780         // auto node expand check
13781         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13782             this.queueExpand(node);
13783         }else if(pt != "append"){
13784             this.cancelExpand();
13785         }
13786         
13787         // set the insert point style on the target node
13788         var returnCls = this.dropNotAllowed;
13789         if(this.isValidDropPoint(n, pt, dd, e, data)){
13790            if(pt){
13791                var el = n.ddel;
13792                var cls;
13793                if(pt == "above"){
13794                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13795                    cls = "x-tree-drag-insert-above";
13796                }else if(pt == "below"){
13797                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13798                    cls = "x-tree-drag-insert-below";
13799                }else{
13800                    returnCls = "x-tree-drop-ok-append";
13801                    cls = "x-tree-drag-append";
13802                }
13803                if(this.lastInsertClass != cls){
13804                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13805                    this.lastInsertClass = cls;
13806                }
13807            }
13808        }
13809        return returnCls;
13810     },
13811     
13812     onNodeOut : function(n, dd, e, data){
13813         
13814         this.cancelExpand();
13815         this.removeDropIndicators(n);
13816     },
13817     
13818     onNodeDrop : function(n, dd, e, data){
13819         var point = this.getDropPoint(e, n, dd);
13820         var targetNode = n.node;
13821         targetNode.ui.startDrop();
13822         if(!this.isValidDropPoint(n, point, dd, e, data)){
13823             targetNode.ui.endDrop();
13824             return false;
13825         }
13826         // first try to find the drop node
13827         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13828         var dropEvent = {
13829             tree : this.tree,
13830             target: targetNode,
13831             data: data,
13832             point: point,
13833             source: dd,
13834             rawEvent: e,
13835             dropNode: dropNode,
13836             cancel: !dropNode   
13837         };
13838         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13839         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13840             targetNode.ui.endDrop();
13841             return false;
13842         }
13843         // allow target changing
13844         targetNode = dropEvent.target;
13845         if(point == "append" && !targetNode.isExpanded()){
13846             targetNode.expand(false, null, function(){
13847                 this.completeDrop(dropEvent);
13848             }.createDelegate(this));
13849         }else{
13850             this.completeDrop(dropEvent);
13851         }
13852         return true;
13853     },
13854     
13855     completeDrop : function(de){
13856         var ns = de.dropNode, p = de.point, t = de.target;
13857         if(!(ns instanceof Array)){
13858             ns = [ns];
13859         }
13860         var n;
13861         for(var i = 0, len = ns.length; i < len; i++){
13862             n = ns[i];
13863             if(p == "above"){
13864                 t.parentNode.insertBefore(n, t);
13865             }else if(p == "below"){
13866                 t.parentNode.insertBefore(n, t.nextSibling);
13867             }else{
13868                 t.appendChild(n);
13869             }
13870         }
13871         n.ui.focus();
13872         if(this.tree.hlDrop){
13873             n.ui.highlight();
13874         }
13875         t.ui.endDrop();
13876         this.tree.fireEvent("nodedrop", de);
13877     },
13878     
13879     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13880         if(this.tree.hlDrop){
13881             dropNode.ui.focus();
13882             dropNode.ui.highlight();
13883         }
13884         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13885     },
13886     
13887     getTree : function(){
13888         return this.tree;
13889     },
13890     
13891     removeDropIndicators : function(n){
13892         if(n && n.ddel){
13893             var el = n.ddel;
13894             Roo.fly(el).removeClass([
13895                     "x-tree-drag-insert-above",
13896                     "x-tree-drag-insert-below",
13897                     "x-tree-drag-append"]);
13898             this.lastInsertClass = "_noclass";
13899         }
13900     },
13901     
13902     beforeDragDrop : function(target, e, id){
13903         this.cancelExpand();
13904         return true;
13905     },
13906     
13907     afterRepair : function(data){
13908         if(data && Roo.enableFx){
13909             data.node.ui.highlight();
13910         }
13911         this.hideProxy();
13912     } 
13913     
13914 });
13915
13916 }
13917 /*
13918  * Based on:
13919  * Ext JS Library 1.1.1
13920  * Copyright(c) 2006-2007, Ext JS, LLC.
13921  *
13922  * Originally Released Under LGPL - original licence link has changed is not relivant.
13923  *
13924  * Fork - LGPL
13925  * <script type="text/javascript">
13926  */
13927  
13928
13929 if(Roo.dd.DragZone){
13930 Roo.tree.TreeDragZone = function(tree, config){
13931     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13932     this.tree = tree;
13933 };
13934
13935 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13936     ddGroup : "TreeDD",
13937    
13938     onBeforeDrag : function(data, e){
13939         var n = data.node;
13940         return n && n.draggable && !n.disabled;
13941     },
13942      
13943     
13944     onInitDrag : function(e){
13945         var data = this.dragData;
13946         this.tree.getSelectionModel().select(data.node);
13947         this.proxy.update("");
13948         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13949         this.tree.fireEvent("startdrag", this.tree, data.node, e);
13950     },
13951     
13952     getRepairXY : function(e, data){
13953         return data.node.ui.getDDRepairXY();
13954     },
13955     
13956     onEndDrag : function(data, e){
13957         this.tree.fireEvent("enddrag", this.tree, data.node, e);
13958         
13959         
13960     },
13961     
13962     onValidDrop : function(dd, e, id){
13963         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13964         this.hideProxy();
13965     },
13966     
13967     beforeInvalidDrop : function(e, id){
13968         // this scrolls the original position back into view
13969         var sm = this.tree.getSelectionModel();
13970         sm.clearSelections();
13971         sm.select(this.dragData.node);
13972     }
13973 });
13974 }/*
13975  * Based on:
13976  * Ext JS Library 1.1.1
13977  * Copyright(c) 2006-2007, Ext JS, LLC.
13978  *
13979  * Originally Released Under LGPL - original licence link has changed is not relivant.
13980  *
13981  * Fork - LGPL
13982  * <script type="text/javascript">
13983  */
13984 /**
13985  * @class Roo.tree.TreeEditor
13986  * @extends Roo.Editor
13987  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
13988  * as the editor field.
13989  * @constructor
13990  * @param {Object} config (used to be the tree panel.)
13991  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
13992  * 
13993  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
13994  * @cfg {Roo.form.TextField} field [required] The field configuration
13995  *
13996  * 
13997  */
13998 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
13999     var tree = config;
14000     var field;
14001     if (oldconfig) { // old style..
14002         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14003     } else {
14004         // new style..
14005         tree = config.tree;
14006         config.field = config.field  || {};
14007         config.field.xtype = 'TextField';
14008         field = Roo.factory(config.field, Roo.form);
14009     }
14010     config = config || {};
14011     
14012     
14013     this.addEvents({
14014         /**
14015          * @event beforenodeedit
14016          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14017          * false from the handler of this event.
14018          * @param {Editor} this
14019          * @param {Roo.tree.Node} node 
14020          */
14021         "beforenodeedit" : true
14022     });
14023     
14024     //Roo.log(config);
14025     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14026
14027     this.tree = tree;
14028
14029     tree.on('beforeclick', this.beforeNodeClick, this);
14030     tree.getTreeEl().on('mousedown', this.hide, this);
14031     this.on('complete', this.updateNode, this);
14032     this.on('beforestartedit', this.fitToTree, this);
14033     this.on('startedit', this.bindScroll, this, {delay:10});
14034     this.on('specialkey', this.onSpecialKey, this);
14035 };
14036
14037 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14038     /**
14039      * @cfg {String} alignment
14040      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14041      */
14042     alignment: "l-l",
14043     // inherit
14044     autoSize: false,
14045     /**
14046      * @cfg {Boolean} hideEl
14047      * True to hide the bound element while the editor is displayed (defaults to false)
14048      */
14049     hideEl : false,
14050     /**
14051      * @cfg {String} cls
14052      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14053      */
14054     cls: "x-small-editor x-tree-editor",
14055     /**
14056      * @cfg {Boolean} shim
14057      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14058      */
14059     shim:false,
14060     // inherit
14061     shadow:"frame",
14062     /**
14063      * @cfg {Number} maxWidth
14064      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14065      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14066      * scroll and client offsets into account prior to each edit.
14067      */
14068     maxWidth: 250,
14069
14070     editDelay : 350,
14071
14072     // private
14073     fitToTree : function(ed, el){
14074         var td = this.tree.getTreeEl().dom, nd = el.dom;
14075         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14076             td.scrollLeft = nd.offsetLeft;
14077         }
14078         var w = Math.min(
14079                 this.maxWidth,
14080                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14081         this.setSize(w, '');
14082         
14083         return this.fireEvent('beforenodeedit', this, this.editNode);
14084         
14085     },
14086
14087     // private
14088     triggerEdit : function(node){
14089         this.completeEdit();
14090         this.editNode = node;
14091         this.startEdit(node.ui.textNode, node.text);
14092     },
14093
14094     // private
14095     bindScroll : function(){
14096         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14097     },
14098
14099     // private
14100     beforeNodeClick : function(node, e){
14101         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14102         this.lastClick = new Date();
14103         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14104             e.stopEvent();
14105             this.triggerEdit(node);
14106             return false;
14107         }
14108         return true;
14109     },
14110
14111     // private
14112     updateNode : function(ed, value){
14113         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14114         this.editNode.setText(value);
14115     },
14116
14117     // private
14118     onHide : function(){
14119         Roo.tree.TreeEditor.superclass.onHide.call(this);
14120         if(this.editNode){
14121             this.editNode.ui.focus();
14122         }
14123     },
14124
14125     // private
14126     onSpecialKey : function(field, e){
14127         var k = e.getKey();
14128         if(k == e.ESC){
14129             e.stopEvent();
14130             this.cancelEdit();
14131         }else if(k == e.ENTER && !e.hasModifier()){
14132             e.stopEvent();
14133             this.completeEdit();
14134         }
14135     }
14136 });//<Script type="text/javascript">
14137 /*
14138  * Based on:
14139  * Ext JS Library 1.1.1
14140  * Copyright(c) 2006-2007, Ext JS, LLC.
14141  *
14142  * Originally Released Under LGPL - original licence link has changed is not relivant.
14143  *
14144  * Fork - LGPL
14145  * <script type="text/javascript">
14146  */
14147  
14148 /**
14149  * Not documented??? - probably should be...
14150  */
14151
14152 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14153     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14154     
14155     renderElements : function(n, a, targetNode, bulkRender){
14156         //consel.log("renderElements?");
14157         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14158
14159         var t = n.getOwnerTree();
14160         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14161         
14162         var cols = t.columns;
14163         var bw = t.borderWidth;
14164         var c = cols[0];
14165         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14166          var cb = typeof a.checked == "boolean";
14167         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14168         var colcls = 'x-t-' + tid + '-c0';
14169         var buf = [
14170             '<li class="x-tree-node">',
14171             
14172                 
14173                 '<div class="x-tree-node-el ', a.cls,'">',
14174                     // extran...
14175                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14176                 
14177                 
14178                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14179                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14180                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14181                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14182                            (a.iconCls ? ' '+a.iconCls : ''),
14183                            '" unselectable="on" />',
14184                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14185                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14186                              
14187                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14188                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14189                             '<span unselectable="on" qtip="' + tx + '">',
14190                              tx,
14191                              '</span></a>' ,
14192                     '</div>',
14193                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14194                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14195                  ];
14196         for(var i = 1, len = cols.length; i < len; i++){
14197             c = cols[i];
14198             colcls = 'x-t-' + tid + '-c' +i;
14199             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14200             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14201                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14202                       "</div>");
14203          }
14204          
14205          buf.push(
14206             '</a>',
14207             '<div class="x-clear"></div></div>',
14208             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14209             "</li>");
14210         
14211         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14212             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14213                                 n.nextSibling.ui.getEl(), buf.join(""));
14214         }else{
14215             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14216         }
14217         var el = this.wrap.firstChild;
14218         this.elRow = el;
14219         this.elNode = el.firstChild;
14220         this.ranchor = el.childNodes[1];
14221         this.ctNode = this.wrap.childNodes[1];
14222         var cs = el.firstChild.childNodes;
14223         this.indentNode = cs[0];
14224         this.ecNode = cs[1];
14225         this.iconNode = cs[2];
14226         var index = 3;
14227         if(cb){
14228             this.checkbox = cs[3];
14229             index++;
14230         }
14231         this.anchor = cs[index];
14232         
14233         this.textNode = cs[index].firstChild;
14234         
14235         //el.on("click", this.onClick, this);
14236         //el.on("dblclick", this.onDblClick, this);
14237         
14238         
14239        // console.log(this);
14240     },
14241     initEvents : function(){
14242         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14243         
14244             
14245         var a = this.ranchor;
14246
14247         var el = Roo.get(a);
14248
14249         if(Roo.isOpera){ // opera render bug ignores the CSS
14250             el.setStyle("text-decoration", "none");
14251         }
14252
14253         el.on("click", this.onClick, this);
14254         el.on("dblclick", this.onDblClick, this);
14255         el.on("contextmenu", this.onContextMenu, this);
14256         
14257     },
14258     
14259     /*onSelectedChange : function(state){
14260         if(state){
14261             this.focus();
14262             this.addClass("x-tree-selected");
14263         }else{
14264             //this.blur();
14265             this.removeClass("x-tree-selected");
14266         }
14267     },*/
14268     addClass : function(cls){
14269         if(this.elRow){
14270             Roo.fly(this.elRow).addClass(cls);
14271         }
14272         
14273     },
14274     
14275     
14276     removeClass : function(cls){
14277         if(this.elRow){
14278             Roo.fly(this.elRow).removeClass(cls);
14279         }
14280     }
14281
14282     
14283     
14284 });//<Script type="text/javascript">
14285
14286 /*
14287  * Based on:
14288  * Ext JS Library 1.1.1
14289  * Copyright(c) 2006-2007, Ext JS, LLC.
14290  *
14291  * Originally Released Under LGPL - original licence link has changed is not relivant.
14292  *
14293  * Fork - LGPL
14294  * <script type="text/javascript">
14295  */
14296  
14297
14298 /**
14299  * @class Roo.tree.ColumnTree
14300  * @extends Roo.tree.TreePanel
14301  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14302  * @cfg {int} borderWidth  compined right/left border allowance
14303  * @constructor
14304  * @param {String/HTMLElement/Element} el The container element
14305  * @param {Object} config
14306  */
14307 Roo.tree.ColumnTree =  function(el, config)
14308 {
14309    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14310    this.addEvents({
14311         /**
14312         * @event resize
14313         * Fire this event on a container when it resizes
14314         * @param {int} w Width
14315         * @param {int} h Height
14316         */
14317        "resize" : true
14318     });
14319     this.on('resize', this.onResize, this);
14320 };
14321
14322 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14323     //lines:false,
14324     
14325     
14326     borderWidth: Roo.isBorderBox ? 0 : 2, 
14327     headEls : false,
14328     
14329     render : function(){
14330         // add the header.....
14331        
14332         Roo.tree.ColumnTree.superclass.render.apply(this);
14333         
14334         this.el.addClass('x-column-tree');
14335         
14336         this.headers = this.el.createChild(
14337             {cls:'x-tree-headers'},this.innerCt.dom);
14338    
14339         var cols = this.columns, c;
14340         var totalWidth = 0;
14341         this.headEls = [];
14342         var  len = cols.length;
14343         for(var i = 0; i < len; i++){
14344              c = cols[i];
14345              totalWidth += c.width;
14346             this.headEls.push(this.headers.createChild({
14347                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14348                  cn: {
14349                      cls:'x-tree-hd-text',
14350                      html: c.header
14351                  },
14352                  style:'width:'+(c.width-this.borderWidth)+'px;'
14353              }));
14354         }
14355         this.headers.createChild({cls:'x-clear'});
14356         // prevent floats from wrapping when clipped
14357         this.headers.setWidth(totalWidth);
14358         //this.innerCt.setWidth(totalWidth);
14359         this.innerCt.setStyle({ overflow: 'auto' });
14360         this.onResize(this.width, this.height);
14361              
14362         
14363     },
14364     onResize : function(w,h)
14365     {
14366         this.height = h;
14367         this.width = w;
14368         // resize cols..
14369         this.innerCt.setWidth(this.width);
14370         this.innerCt.setHeight(this.height-20);
14371         
14372         // headers...
14373         var cols = this.columns, c;
14374         var totalWidth = 0;
14375         var expEl = false;
14376         var len = cols.length;
14377         for(var i = 0; i < len; i++){
14378             c = cols[i];
14379             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14380                 // it's the expander..
14381                 expEl  = this.headEls[i];
14382                 continue;
14383             }
14384             totalWidth += c.width;
14385             
14386         }
14387         if (expEl) {
14388             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14389         }
14390         this.headers.setWidth(w-20);
14391
14392         
14393         
14394         
14395     }
14396 });
14397 /*
14398  * Based on:
14399  * Ext JS Library 1.1.1
14400  * Copyright(c) 2006-2007, Ext JS, LLC.
14401  *
14402  * Originally Released Under LGPL - original licence link has changed is not relivant.
14403  *
14404  * Fork - LGPL
14405  * <script type="text/javascript">
14406  */
14407  
14408 /**
14409  * @class Roo.menu.Menu
14410  * @extends Roo.util.Observable
14411  * @children Roo.menu.BaseItem
14412  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14413  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14414  * @constructor
14415  * Creates a new Menu
14416  * @param {Object} config Configuration options
14417  */
14418 Roo.menu.Menu = function(config){
14419     
14420     Roo.menu.Menu.superclass.constructor.call(this, config);
14421     
14422     this.id = this.id || Roo.id();
14423     this.addEvents({
14424         /**
14425          * @event beforeshow
14426          * Fires before this menu is displayed
14427          * @param {Roo.menu.Menu} this
14428          */
14429         beforeshow : true,
14430         /**
14431          * @event beforehide
14432          * Fires before this menu is hidden
14433          * @param {Roo.menu.Menu} this
14434          */
14435         beforehide : true,
14436         /**
14437          * @event show
14438          * Fires after this menu is displayed
14439          * @param {Roo.menu.Menu} this
14440          */
14441         show : true,
14442         /**
14443          * @event hide
14444          * Fires after this menu is hidden
14445          * @param {Roo.menu.Menu} this
14446          */
14447         hide : true,
14448         /**
14449          * @event click
14450          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14451          * @param {Roo.menu.Menu} this
14452          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14453          * @param {Roo.EventObject} e
14454          */
14455         click : true,
14456         /**
14457          * @event mouseover
14458          * Fires when the mouse is hovering over this menu
14459          * @param {Roo.menu.Menu} this
14460          * @param {Roo.EventObject} e
14461          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14462          */
14463         mouseover : true,
14464         /**
14465          * @event mouseout
14466          * Fires when the mouse exits this menu
14467          * @param {Roo.menu.Menu} this
14468          * @param {Roo.EventObject} e
14469          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14470          */
14471         mouseout : true,
14472         /**
14473          * @event itemclick
14474          * Fires when a menu item contained in this menu is clicked
14475          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14476          * @param {Roo.EventObject} e
14477          */
14478         itemclick: true
14479     });
14480     if (this.registerMenu) {
14481         Roo.menu.MenuMgr.register(this);
14482     }
14483     
14484     var mis = this.items;
14485     this.items = new Roo.util.MixedCollection();
14486     if(mis){
14487         this.add.apply(this, mis);
14488     }
14489 };
14490
14491 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14492     /**
14493      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14494      */
14495     minWidth : 120,
14496     /**
14497      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14498      * for bottom-right shadow (defaults to "sides")
14499      */
14500     shadow : "sides",
14501     /**
14502      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14503      * this menu (defaults to "tl-tr?")
14504      */
14505     subMenuAlign : "tl-tr?",
14506     /**
14507      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14508      * relative to its element of origin (defaults to "tl-bl?")
14509      */
14510     defaultAlign : "tl-bl?",
14511     /**
14512      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14513      */
14514     allowOtherMenus : false,
14515     /**
14516      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14517      */
14518     registerMenu : true,
14519
14520     hidden:true,
14521
14522     // private
14523     render : function(){
14524         if(this.el){
14525             return;
14526         }
14527         var el = this.el = new Roo.Layer({
14528             cls: "x-menu",
14529             shadow:this.shadow,
14530             constrain: false,
14531             parentEl: this.parentEl || document.body,
14532             zindex:15000
14533         });
14534
14535         this.keyNav = new Roo.menu.MenuNav(this);
14536
14537         if(this.plain){
14538             el.addClass("x-menu-plain");
14539         }
14540         if(this.cls){
14541             el.addClass(this.cls);
14542         }
14543         // generic focus element
14544         this.focusEl = el.createChild({
14545             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14546         });
14547         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14548         //disabling touch- as it's causing issues ..
14549         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14550         ul.on('click'   , this.onClick, this);
14551         
14552         
14553         ul.on("mouseover", this.onMouseOver, this);
14554         ul.on("mouseout", this.onMouseOut, this);
14555         this.items.each(function(item){
14556             if (item.hidden) {
14557                 return;
14558             }
14559             
14560             var li = document.createElement("li");
14561             li.className = "x-menu-list-item";
14562             ul.dom.appendChild(li);
14563             item.render(li, this);
14564         }, this);
14565         this.ul = ul;
14566         this.autoWidth();
14567     },
14568
14569     // private
14570     autoWidth : function(){
14571         var el = this.el, ul = this.ul;
14572         if(!el){
14573             return;
14574         }
14575         var w = this.width;
14576         if(w){
14577             el.setWidth(w);
14578         }else if(Roo.isIE){
14579             el.setWidth(this.minWidth);
14580             var t = el.dom.offsetWidth; // force recalc
14581             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14582         }
14583     },
14584
14585     // private
14586     delayAutoWidth : function(){
14587         if(this.rendered){
14588             if(!this.awTask){
14589                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14590             }
14591             this.awTask.delay(20);
14592         }
14593     },
14594
14595     // private
14596     findTargetItem : function(e){
14597         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14598         if(t && t.menuItemId){
14599             return this.items.get(t.menuItemId);
14600         }
14601     },
14602
14603     // private
14604     onClick : function(e){
14605         Roo.log("menu.onClick");
14606         var t = this.findTargetItem(e);
14607         if(!t){
14608             return;
14609         }
14610         Roo.log(e);
14611         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14612             if(t == this.activeItem && t.shouldDeactivate(e)){
14613                 this.activeItem.deactivate();
14614                 delete this.activeItem;
14615                 return;
14616             }
14617             if(t.canActivate){
14618                 this.setActiveItem(t, true);
14619             }
14620             return;
14621             
14622             
14623         }
14624         
14625         t.onClick(e);
14626         this.fireEvent("click", this, t, e);
14627     },
14628
14629     // private
14630     setActiveItem : function(item, autoExpand){
14631         if(item != this.activeItem){
14632             if(this.activeItem){
14633                 this.activeItem.deactivate();
14634             }
14635             this.activeItem = item;
14636             item.activate(autoExpand);
14637         }else if(autoExpand){
14638             item.expandMenu();
14639         }
14640     },
14641
14642     // private
14643     tryActivate : function(start, step){
14644         var items = this.items;
14645         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14646             var item = items.get(i);
14647             if(!item.disabled && item.canActivate){
14648                 this.setActiveItem(item, false);
14649                 return item;
14650             }
14651         }
14652         return false;
14653     },
14654
14655     // private
14656     onMouseOver : function(e){
14657         var t;
14658         if(t = this.findTargetItem(e)){
14659             if(t.canActivate && !t.disabled){
14660                 this.setActiveItem(t, true);
14661             }
14662         }
14663         this.fireEvent("mouseover", this, e, t);
14664     },
14665
14666     // private
14667     onMouseOut : function(e){
14668         var t;
14669         if(t = this.findTargetItem(e)){
14670             if(t == this.activeItem && t.shouldDeactivate(e)){
14671                 this.activeItem.deactivate();
14672                 delete this.activeItem;
14673             }
14674         }
14675         this.fireEvent("mouseout", this, e, t);
14676     },
14677
14678     /**
14679      * Read-only.  Returns true if the menu is currently displayed, else false.
14680      * @type Boolean
14681      */
14682     isVisible : function(){
14683         return this.el && !this.hidden;
14684     },
14685
14686     /**
14687      * Displays this menu relative to another element
14688      * @param {String/HTMLElement/Roo.Element} element The element to align to
14689      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14690      * the element (defaults to this.defaultAlign)
14691      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14692      */
14693     show : function(el, pos, parentMenu){
14694         this.parentMenu = parentMenu;
14695         if(!this.el){
14696             this.render();
14697         }
14698         this.fireEvent("beforeshow", this);
14699         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14700     },
14701
14702     /**
14703      * Displays this menu at a specific xy position
14704      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14705      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14706      */
14707     showAt : function(xy, parentMenu, /* private: */_e){
14708         this.parentMenu = parentMenu;
14709         if(!this.el){
14710             this.render();
14711         }
14712         if(_e !== false){
14713             this.fireEvent("beforeshow", this);
14714             xy = this.el.adjustForConstraints(xy);
14715         }
14716         this.el.setXY(xy);
14717         this.el.show();
14718         this.hidden = false;
14719         this.focus();
14720         this.fireEvent("show", this);
14721     },
14722
14723     focus : function(){
14724         if(!this.hidden){
14725             this.doFocus.defer(50, this);
14726         }
14727     },
14728
14729     doFocus : function(){
14730         if(!this.hidden){
14731             this.focusEl.focus();
14732         }
14733     },
14734
14735     /**
14736      * Hides this menu and optionally all parent menus
14737      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14738      */
14739     hide : function(deep){
14740         if(this.el && this.isVisible()){
14741             this.fireEvent("beforehide", this);
14742             if(this.activeItem){
14743                 this.activeItem.deactivate();
14744                 this.activeItem = null;
14745             }
14746             this.el.hide();
14747             this.hidden = true;
14748             this.fireEvent("hide", this);
14749         }
14750         if(deep === true && this.parentMenu){
14751             this.parentMenu.hide(true);
14752         }
14753     },
14754
14755     /**
14756      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14757      * Any of the following are valid:
14758      * <ul>
14759      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14760      * <li>An HTMLElement object which will be converted to a menu item</li>
14761      * <li>A menu item config object that will be created as a new menu item</li>
14762      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14763      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14764      * </ul>
14765      * Usage:
14766      * <pre><code>
14767 // Create the menu
14768 var menu = new Roo.menu.Menu();
14769
14770 // Create a menu item to add by reference
14771 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14772
14773 // Add a bunch of items at once using different methods.
14774 // Only the last item added will be returned.
14775 var item = menu.add(
14776     menuItem,                // add existing item by ref
14777     'Dynamic Item',          // new TextItem
14778     '-',                     // new separator
14779     { text: 'Config Item' }  // new item by config
14780 );
14781 </code></pre>
14782      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14783      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14784      */
14785     add : function(){
14786         var a = arguments, l = a.length, item;
14787         for(var i = 0; i < l; i++){
14788             var el = a[i];
14789             if ((typeof(el) == "object") && el.xtype && el.xns) {
14790                 el = Roo.factory(el, Roo.menu);
14791             }
14792             
14793             if(el.render){ // some kind of Item
14794                 item = this.addItem(el);
14795             }else if(typeof el == "string"){ // string
14796                 if(el == "separator" || el == "-"){
14797                     item = this.addSeparator();
14798                 }else{
14799                     item = this.addText(el);
14800                 }
14801             }else if(el.tagName || el.el){ // element
14802                 item = this.addElement(el);
14803             }else if(typeof el == "object"){ // must be menu item config?
14804                 item = this.addMenuItem(el);
14805             }
14806         }
14807         return item;
14808     },
14809
14810     /**
14811      * Returns this menu's underlying {@link Roo.Element} object
14812      * @return {Roo.Element} The element
14813      */
14814     getEl : function(){
14815         if(!this.el){
14816             this.render();
14817         }
14818         return this.el;
14819     },
14820
14821     /**
14822      * Adds a separator bar to the menu
14823      * @return {Roo.menu.Item} The menu item that was added
14824      */
14825     addSeparator : function(){
14826         return this.addItem(new Roo.menu.Separator());
14827     },
14828
14829     /**
14830      * Adds an {@link Roo.Element} object to the menu
14831      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14832      * @return {Roo.menu.Item} The menu item that was added
14833      */
14834     addElement : function(el){
14835         return this.addItem(new Roo.menu.BaseItem(el));
14836     },
14837
14838     /**
14839      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14840      * @param {Roo.menu.Item} item The menu item to add
14841      * @return {Roo.menu.Item} The menu item that was added
14842      */
14843     addItem : function(item){
14844         this.items.add(item);
14845         if(this.ul){
14846             var li = document.createElement("li");
14847             li.className = "x-menu-list-item";
14848             this.ul.dom.appendChild(li);
14849             item.render(li, this);
14850             this.delayAutoWidth();
14851         }
14852         return item;
14853     },
14854
14855     /**
14856      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14857      * @param {Object} config A MenuItem config object
14858      * @return {Roo.menu.Item} The menu item that was added
14859      */
14860     addMenuItem : function(config){
14861         if(!(config instanceof Roo.menu.Item)){
14862             if(typeof config.checked == "boolean"){ // must be check menu item config?
14863                 config = new Roo.menu.CheckItem(config);
14864             }else{
14865                 config = new Roo.menu.Item(config);
14866             }
14867         }
14868         return this.addItem(config);
14869     },
14870
14871     /**
14872      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14873      * @param {String} text The text to display in the menu item
14874      * @return {Roo.menu.Item} The menu item that was added
14875      */
14876     addText : function(text){
14877         return this.addItem(new Roo.menu.TextItem({ text : text }));
14878     },
14879
14880     /**
14881      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14882      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14883      * @param {Roo.menu.Item} item The menu item to add
14884      * @return {Roo.menu.Item} The menu item that was added
14885      */
14886     insert : function(index, item){
14887         this.items.insert(index, item);
14888         if(this.ul){
14889             var li = document.createElement("li");
14890             li.className = "x-menu-list-item";
14891             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14892             item.render(li, this);
14893             this.delayAutoWidth();
14894         }
14895         return item;
14896     },
14897
14898     /**
14899      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14900      * @param {Roo.menu.Item} item The menu item to remove
14901      */
14902     remove : function(item){
14903         this.items.removeKey(item.id);
14904         item.destroy();
14905     },
14906
14907     /**
14908      * Removes and destroys all items in the menu
14909      */
14910     removeAll : function(){
14911         var f;
14912         while(f = this.items.first()){
14913             this.remove(f);
14914         }
14915     }
14916 });
14917
14918 // MenuNav is a private utility class used internally by the Menu
14919 Roo.menu.MenuNav = function(menu){
14920     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14921     this.scope = this.menu = menu;
14922 };
14923
14924 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14925     doRelay : function(e, h){
14926         var k = e.getKey();
14927         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14928             this.menu.tryActivate(0, 1);
14929             return false;
14930         }
14931         return h.call(this.scope || this, e, this.menu);
14932     },
14933
14934     up : function(e, m){
14935         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14936             m.tryActivate(m.items.length-1, -1);
14937         }
14938     },
14939
14940     down : function(e, m){
14941         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14942             m.tryActivate(0, 1);
14943         }
14944     },
14945
14946     right : function(e, m){
14947         if(m.activeItem){
14948             m.activeItem.expandMenu(true);
14949         }
14950     },
14951
14952     left : function(e, m){
14953         m.hide();
14954         if(m.parentMenu && m.parentMenu.activeItem){
14955             m.parentMenu.activeItem.activate();
14956         }
14957     },
14958
14959     enter : function(e, m){
14960         if(m.activeItem){
14961             e.stopPropagation();
14962             m.activeItem.onClick(e);
14963             m.fireEvent("click", this, m.activeItem);
14964             return true;
14965         }
14966     }
14967 });/*
14968  * Based on:
14969  * Ext JS Library 1.1.1
14970  * Copyright(c) 2006-2007, Ext JS, LLC.
14971  *
14972  * Originally Released Under LGPL - original licence link has changed is not relivant.
14973  *
14974  * Fork - LGPL
14975  * <script type="text/javascript">
14976  */
14977  
14978 /**
14979  * @class Roo.menu.MenuMgr
14980  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
14981  * @static
14982  */
14983 Roo.menu.MenuMgr = function(){
14984    var menus, active, groups = {}, attached = false, lastShow = new Date();
14985
14986    // private - called when first menu is created
14987    function init(){
14988        menus = {};
14989        active = new Roo.util.MixedCollection();
14990        Roo.get(document).addKeyListener(27, function(){
14991            if(active.length > 0){
14992                hideAll();
14993            }
14994        });
14995    }
14996
14997    // private
14998    function hideAll(){
14999        if(active && active.length > 0){
15000            var c = active.clone();
15001            c.each(function(m){
15002                m.hide();
15003            });
15004        }
15005    }
15006
15007    // private
15008    function onHide(m){
15009        active.remove(m);
15010        if(active.length < 1){
15011            Roo.get(document).un("mousedown", onMouseDown);
15012            attached = false;
15013        }
15014    }
15015
15016    // private
15017    function onShow(m){
15018        var last = active.last();
15019        lastShow = new Date();
15020        active.add(m);
15021        if(!attached){
15022            Roo.get(document).on("mousedown", onMouseDown);
15023            attached = true;
15024        }
15025        if(m.parentMenu){
15026           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15027           m.parentMenu.activeChild = m;
15028        }else if(last && last.isVisible()){
15029           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15030        }
15031    }
15032
15033    // private
15034    function onBeforeHide(m){
15035        if(m.activeChild){
15036            m.activeChild.hide();
15037        }
15038        if(m.autoHideTimer){
15039            clearTimeout(m.autoHideTimer);
15040            delete m.autoHideTimer;
15041        }
15042    }
15043
15044    // private
15045    function onBeforeShow(m){
15046        var pm = m.parentMenu;
15047        if(!pm && !m.allowOtherMenus){
15048            hideAll();
15049        }else if(pm && pm.activeChild && active != m){
15050            pm.activeChild.hide();
15051        }
15052    }
15053
15054    // private
15055    function onMouseDown(e){
15056        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15057            hideAll();
15058        }
15059    }
15060
15061    // private
15062    function onBeforeCheck(mi, state){
15063        if(state){
15064            var g = groups[mi.group];
15065            for(var i = 0, l = g.length; i < l; i++){
15066                if(g[i] != mi){
15067                    g[i].setChecked(false);
15068                }
15069            }
15070        }
15071    }
15072
15073    return {
15074
15075        /**
15076         * Hides all menus that are currently visible
15077         */
15078        hideAll : function(){
15079             hideAll();  
15080        },
15081
15082        // private
15083        register : function(menu){
15084            if(!menus){
15085                init();
15086            }
15087            menus[menu.id] = menu;
15088            menu.on("beforehide", onBeforeHide);
15089            menu.on("hide", onHide);
15090            menu.on("beforeshow", onBeforeShow);
15091            menu.on("show", onShow);
15092            var g = menu.group;
15093            if(g && menu.events["checkchange"]){
15094                if(!groups[g]){
15095                    groups[g] = [];
15096                }
15097                groups[g].push(menu);
15098                menu.on("checkchange", onCheck);
15099            }
15100        },
15101
15102         /**
15103          * Returns a {@link Roo.menu.Menu} object
15104          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15105          * be used to generate and return a new Menu instance.
15106          */
15107        get : function(menu){
15108            if(typeof menu == "string"){ // menu id
15109                return menus[menu];
15110            }else if(menu.events){  // menu instance
15111                return menu;
15112            }else if(typeof menu.length == 'number'){ // array of menu items?
15113                return new Roo.menu.Menu({items:menu});
15114            }else{ // otherwise, must be a config
15115                return new Roo.menu.Menu(menu);
15116            }
15117        },
15118
15119        // private
15120        unregister : function(menu){
15121            delete menus[menu.id];
15122            menu.un("beforehide", onBeforeHide);
15123            menu.un("hide", onHide);
15124            menu.un("beforeshow", onBeforeShow);
15125            menu.un("show", onShow);
15126            var g = menu.group;
15127            if(g && menu.events["checkchange"]){
15128                groups[g].remove(menu);
15129                menu.un("checkchange", onCheck);
15130            }
15131        },
15132
15133        // private
15134        registerCheckable : function(menuItem){
15135            var g = menuItem.group;
15136            if(g){
15137                if(!groups[g]){
15138                    groups[g] = [];
15139                }
15140                groups[g].push(menuItem);
15141                menuItem.on("beforecheckchange", onBeforeCheck);
15142            }
15143        },
15144
15145        // private
15146        unregisterCheckable : function(menuItem){
15147            var g = menuItem.group;
15148            if(g){
15149                groups[g].remove(menuItem);
15150                menuItem.un("beforecheckchange", onBeforeCheck);
15151            }
15152        }
15153    };
15154 }();/*
15155  * Based on:
15156  * Ext JS Library 1.1.1
15157  * Copyright(c) 2006-2007, Ext JS, LLC.
15158  *
15159  * Originally Released Under LGPL - original licence link has changed is not relivant.
15160  *
15161  * Fork - LGPL
15162  * <script type="text/javascript">
15163  */
15164  
15165
15166 /**
15167  * @class Roo.menu.BaseItem
15168  * @extends Roo.Component
15169  * @abstract
15170  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15171  * management and base configuration options shared by all menu components.
15172  * @constructor
15173  * Creates a new BaseItem
15174  * @param {Object} config Configuration options
15175  */
15176 Roo.menu.BaseItem = function(config){
15177     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15178
15179     this.addEvents({
15180         /**
15181          * @event click
15182          * Fires when this item is clicked
15183          * @param {Roo.menu.BaseItem} this
15184          * @param {Roo.EventObject} e
15185          */
15186         click: true,
15187         /**
15188          * @event activate
15189          * Fires when this item is activated
15190          * @param {Roo.menu.BaseItem} this
15191          */
15192         activate : true,
15193         /**
15194          * @event deactivate
15195          * Fires when this item is deactivated
15196          * @param {Roo.menu.BaseItem} this
15197          */
15198         deactivate : true
15199     });
15200
15201     if(this.handler){
15202         this.on("click", this.handler, this.scope, true);
15203     }
15204 };
15205
15206 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15207     /**
15208      * @cfg {Function} handler
15209      * A function that will handle the click event of this menu item (defaults to undefined)
15210      */
15211     /**
15212      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15213      */
15214     canActivate : false,
15215     
15216      /**
15217      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15218      */
15219     hidden: false,
15220     
15221     /**
15222      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15223      */
15224     activeClass : "x-menu-item-active",
15225     /**
15226      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15227      */
15228     hideOnClick : true,
15229     /**
15230      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15231      */
15232     hideDelay : 100,
15233
15234     // private
15235     ctype: "Roo.menu.BaseItem",
15236
15237     // private
15238     actionMode : "container",
15239
15240     // private
15241     render : function(container, parentMenu){
15242         this.parentMenu = parentMenu;
15243         Roo.menu.BaseItem.superclass.render.call(this, container);
15244         this.container.menuItemId = this.id;
15245     },
15246
15247     // private
15248     onRender : function(container, position){
15249         this.el = Roo.get(this.el);
15250         container.dom.appendChild(this.el.dom);
15251     },
15252
15253     // private
15254     onClick : function(e){
15255         if(!this.disabled && this.fireEvent("click", this, e) !== false
15256                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15257             this.handleClick(e);
15258         }else{
15259             e.stopEvent();
15260         }
15261     },
15262
15263     // private
15264     activate : function(){
15265         if(this.disabled){
15266             return false;
15267         }
15268         var li = this.container;
15269         li.addClass(this.activeClass);
15270         this.region = li.getRegion().adjust(2, 2, -2, -2);
15271         this.fireEvent("activate", this);
15272         return true;
15273     },
15274
15275     // private
15276     deactivate : function(){
15277         this.container.removeClass(this.activeClass);
15278         this.fireEvent("deactivate", this);
15279     },
15280
15281     // private
15282     shouldDeactivate : function(e){
15283         return !this.region || !this.region.contains(e.getPoint());
15284     },
15285
15286     // private
15287     handleClick : function(e){
15288         if(this.hideOnClick){
15289             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15290         }
15291     },
15292
15293     // private
15294     expandMenu : function(autoActivate){
15295         // do nothing
15296     },
15297
15298     // private
15299     hideMenu : function(){
15300         // do nothing
15301     }
15302 });/*
15303  * Based on:
15304  * Ext JS Library 1.1.1
15305  * Copyright(c) 2006-2007, Ext JS, LLC.
15306  *
15307  * Originally Released Under LGPL - original licence link has changed is not relivant.
15308  *
15309  * Fork - LGPL
15310  * <script type="text/javascript">
15311  */
15312  
15313 /**
15314  * @class Roo.menu.Adapter
15315  * @extends Roo.menu.BaseItem
15316  * @abstract
15317  * 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.
15318  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15319  * @constructor
15320  * Creates a new Adapter
15321  * @param {Object} config Configuration options
15322  */
15323 Roo.menu.Adapter = function(component, config){
15324     Roo.menu.Adapter.superclass.constructor.call(this, config);
15325     this.component = component;
15326 };
15327 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15328     // private
15329     canActivate : true,
15330
15331     // private
15332     onRender : function(container, position){
15333         this.component.render(container);
15334         this.el = this.component.getEl();
15335     },
15336
15337     // private
15338     activate : function(){
15339         if(this.disabled){
15340             return false;
15341         }
15342         this.component.focus();
15343         this.fireEvent("activate", this);
15344         return true;
15345     },
15346
15347     // private
15348     deactivate : function(){
15349         this.fireEvent("deactivate", this);
15350     },
15351
15352     // private
15353     disable : function(){
15354         this.component.disable();
15355         Roo.menu.Adapter.superclass.disable.call(this);
15356     },
15357
15358     // private
15359     enable : function(){
15360         this.component.enable();
15361         Roo.menu.Adapter.superclass.enable.call(this);
15362     }
15363 });/*
15364  * Based on:
15365  * Ext JS Library 1.1.1
15366  * Copyright(c) 2006-2007, Ext JS, LLC.
15367  *
15368  * Originally Released Under LGPL - original licence link has changed is not relivant.
15369  *
15370  * Fork - LGPL
15371  * <script type="text/javascript">
15372  */
15373
15374 /**
15375  * @class Roo.menu.TextItem
15376  * @extends Roo.menu.BaseItem
15377  * Adds a static text string to a menu, usually used as either a heading or group separator.
15378  * Note: old style constructor with text is still supported.
15379  * 
15380  * @constructor
15381  * Creates a new TextItem
15382  * @param {Object} cfg Configuration
15383  */
15384 Roo.menu.TextItem = function(cfg){
15385     if (typeof(cfg) == 'string') {
15386         this.text = cfg;
15387     } else {
15388         Roo.apply(this,cfg);
15389     }
15390     
15391     Roo.menu.TextItem.superclass.constructor.call(this);
15392 };
15393
15394 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15395     /**
15396      * @cfg {String} text Text to show on item.
15397      */
15398     text : '',
15399     
15400     /**
15401      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15402      */
15403     hideOnClick : false,
15404     /**
15405      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15406      */
15407     itemCls : "x-menu-text",
15408
15409     // private
15410     onRender : function(){
15411         var s = document.createElement("span");
15412         s.className = this.itemCls;
15413         s.innerHTML = this.text;
15414         this.el = s;
15415         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15416     }
15417 });/*
15418  * Based on:
15419  * Ext JS Library 1.1.1
15420  * Copyright(c) 2006-2007, Ext JS, LLC.
15421  *
15422  * Originally Released Under LGPL - original licence link has changed is not relivant.
15423  *
15424  * Fork - LGPL
15425  * <script type="text/javascript">
15426  */
15427
15428 /**
15429  * @class Roo.menu.Separator
15430  * @extends Roo.menu.BaseItem
15431  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15432  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15433  * @constructor
15434  * @param {Object} config Configuration options
15435  */
15436 Roo.menu.Separator = function(config){
15437     Roo.menu.Separator.superclass.constructor.call(this, config);
15438 };
15439
15440 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15441     /**
15442      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15443      */
15444     itemCls : "x-menu-sep",
15445     /**
15446      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15447      */
15448     hideOnClick : false,
15449
15450     // private
15451     onRender : function(li){
15452         var s = document.createElement("span");
15453         s.className = this.itemCls;
15454         s.innerHTML = "&#160;";
15455         this.el = s;
15456         li.addClass("x-menu-sep-li");
15457         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15458     }
15459 });/*
15460  * Based on:
15461  * Ext JS Library 1.1.1
15462  * Copyright(c) 2006-2007, Ext JS, LLC.
15463  *
15464  * Originally Released Under LGPL - original licence link has changed is not relivant.
15465  *
15466  * Fork - LGPL
15467  * <script type="text/javascript">
15468  */
15469 /**
15470  * @class Roo.menu.Item
15471  * @extends Roo.menu.BaseItem
15472  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15473  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15474  * activation and click handling.
15475  * @constructor
15476  * Creates a new Item
15477  * @param {Object} config Configuration options
15478  */
15479 Roo.menu.Item = function(config){
15480     Roo.menu.Item.superclass.constructor.call(this, config);
15481     if(this.menu){
15482         this.menu = Roo.menu.MenuMgr.get(this.menu);
15483     }
15484 };
15485 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15486     /**
15487      * @cfg {Roo.menu.Menu} menu
15488      * A Sub menu
15489      */
15490     /**
15491      * @cfg {String} text
15492      * The text to show on the menu item.
15493      */
15494     text: '',
15495      /**
15496      * @cfg {String} HTML to render in menu
15497      * The text to show on the menu item (HTML version).
15498      */
15499     html: '',
15500     /**
15501      * @cfg {String} icon
15502      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15503      */
15504     icon: undefined,
15505     /**
15506      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15507      */
15508     itemCls : "x-menu-item",
15509     /**
15510      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15511      */
15512     canActivate : true,
15513     /**
15514      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15515      */
15516     showDelay: 200,
15517     // doc'd in BaseItem
15518     hideDelay: 200,
15519
15520     // private
15521     ctype: "Roo.menu.Item",
15522     
15523     // private
15524     onRender : function(container, position){
15525         var el = document.createElement("a");
15526         el.hideFocus = true;
15527         el.unselectable = "on";
15528         el.href = this.href || "#";
15529         if(this.hrefTarget){
15530             el.target = this.hrefTarget;
15531         }
15532         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15533         
15534         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15535         
15536         el.innerHTML = String.format(
15537                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15538                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15539         this.el = el;
15540         Roo.menu.Item.superclass.onRender.call(this, container, position);
15541     },
15542
15543     /**
15544      * Sets the text to display in this menu item
15545      * @param {String} text The text to display
15546      * @param {Boolean} isHTML true to indicate text is pure html.
15547      */
15548     setText : function(text, isHTML){
15549         if (isHTML) {
15550             this.html = text;
15551         } else {
15552             this.text = text;
15553             this.html = '';
15554         }
15555         if(this.rendered){
15556             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15557      
15558             this.el.update(String.format(
15559                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15560                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15561             this.parentMenu.autoWidth();
15562         }
15563     },
15564
15565     // private
15566     handleClick : function(e){
15567         if(!this.href){ // if no link defined, stop the event automatically
15568             e.stopEvent();
15569         }
15570         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15571     },
15572
15573     // private
15574     activate : function(autoExpand){
15575         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15576             this.focus();
15577             if(autoExpand){
15578                 this.expandMenu();
15579             }
15580         }
15581         return true;
15582     },
15583
15584     // private
15585     shouldDeactivate : function(e){
15586         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15587             if(this.menu && this.menu.isVisible()){
15588                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15589             }
15590             return true;
15591         }
15592         return false;
15593     },
15594
15595     // private
15596     deactivate : function(){
15597         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15598         this.hideMenu();
15599     },
15600
15601     // private
15602     expandMenu : function(autoActivate){
15603         if(!this.disabled && this.menu){
15604             clearTimeout(this.hideTimer);
15605             delete this.hideTimer;
15606             if(!this.menu.isVisible() && !this.showTimer){
15607                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15608             }else if (this.menu.isVisible() && autoActivate){
15609                 this.menu.tryActivate(0, 1);
15610             }
15611         }
15612     },
15613
15614     // private
15615     deferExpand : function(autoActivate){
15616         delete this.showTimer;
15617         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15618         if(autoActivate){
15619             this.menu.tryActivate(0, 1);
15620         }
15621     },
15622
15623     // private
15624     hideMenu : function(){
15625         clearTimeout(this.showTimer);
15626         delete this.showTimer;
15627         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15628             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15629         }
15630     },
15631
15632     // private
15633     deferHide : function(){
15634         delete this.hideTimer;
15635         this.menu.hide();
15636     }
15637 });/*
15638  * Based on:
15639  * Ext JS Library 1.1.1
15640  * Copyright(c) 2006-2007, Ext JS, LLC.
15641  *
15642  * Originally Released Under LGPL - original licence link has changed is not relivant.
15643  *
15644  * Fork - LGPL
15645  * <script type="text/javascript">
15646  */
15647  
15648 /**
15649  * @class Roo.menu.CheckItem
15650  * @extends Roo.menu.Item
15651  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15652  * @constructor
15653  * Creates a new CheckItem
15654  * @param {Object} config Configuration options
15655  */
15656 Roo.menu.CheckItem = function(config){
15657     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15658     this.addEvents({
15659         /**
15660          * @event beforecheckchange
15661          * Fires before the checked value is set, providing an opportunity to cancel if needed
15662          * @param {Roo.menu.CheckItem} this
15663          * @param {Boolean} checked The new checked value that will be set
15664          */
15665         "beforecheckchange" : true,
15666         /**
15667          * @event checkchange
15668          * Fires after the checked value has been set
15669          * @param {Roo.menu.CheckItem} this
15670          * @param {Boolean} checked The checked value that was set
15671          */
15672         "checkchange" : true
15673     });
15674     if(this.checkHandler){
15675         this.on('checkchange', this.checkHandler, this.scope);
15676     }
15677 };
15678 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15679     /**
15680      * @cfg {String} group
15681      * All check items with the same group name will automatically be grouped into a single-select
15682      * radio button group (defaults to '')
15683      */
15684     /**
15685      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15686      */
15687     itemCls : "x-menu-item x-menu-check-item",
15688     /**
15689      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15690      */
15691     groupClass : "x-menu-group-item",
15692
15693     /**
15694      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15695      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15696      * initialized with checked = true will be rendered as checked.
15697      */
15698     checked: false,
15699
15700     // private
15701     ctype: "Roo.menu.CheckItem",
15702
15703     // private
15704     onRender : function(c){
15705         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15706         if(this.group){
15707             this.el.addClass(this.groupClass);
15708         }
15709         Roo.menu.MenuMgr.registerCheckable(this);
15710         if(this.checked){
15711             this.checked = false;
15712             this.setChecked(true, true);
15713         }
15714     },
15715
15716     // private
15717     destroy : function(){
15718         if(this.rendered){
15719             Roo.menu.MenuMgr.unregisterCheckable(this);
15720         }
15721         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15722     },
15723
15724     /**
15725      * Set the checked state of this item
15726      * @param {Boolean} checked The new checked value
15727      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15728      */
15729     setChecked : function(state, suppressEvent){
15730         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15731             if(this.container){
15732                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15733             }
15734             this.checked = state;
15735             if(suppressEvent !== true){
15736                 this.fireEvent("checkchange", this, state);
15737             }
15738         }
15739     },
15740
15741     // private
15742     handleClick : function(e){
15743        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15744            this.setChecked(!this.checked);
15745        }
15746        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15747     }
15748 });/*
15749  * Based on:
15750  * Ext JS Library 1.1.1
15751  * Copyright(c) 2006-2007, Ext JS, LLC.
15752  *
15753  * Originally Released Under LGPL - original licence link has changed is not relivant.
15754  *
15755  * Fork - LGPL
15756  * <script type="text/javascript">
15757  */
15758  
15759 /**
15760  * @class Roo.menu.DateItem
15761  * @extends Roo.menu.Adapter
15762  * A menu item that wraps the {@link Roo.DatPicker} component.
15763  * @constructor
15764  * Creates a new DateItem
15765  * @param {Object} config Configuration options
15766  */
15767 Roo.menu.DateItem = function(config){
15768     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15769     /** The Roo.DatePicker object @type Roo.DatePicker */
15770     this.picker = this.component;
15771     this.addEvents({select: true});
15772     
15773     this.picker.on("render", function(picker){
15774         picker.getEl().swallowEvent("click");
15775         picker.container.addClass("x-menu-date-item");
15776     });
15777
15778     this.picker.on("select", this.onSelect, this);
15779 };
15780
15781 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15782     // private
15783     onSelect : function(picker, date){
15784         this.fireEvent("select", this, date, picker);
15785         Roo.menu.DateItem.superclass.handleClick.call(this);
15786     }
15787 });/*
15788  * Based on:
15789  * Ext JS Library 1.1.1
15790  * Copyright(c) 2006-2007, Ext JS, LLC.
15791  *
15792  * Originally Released Under LGPL - original licence link has changed is not relivant.
15793  *
15794  * Fork - LGPL
15795  * <script type="text/javascript">
15796  */
15797  
15798 /**
15799  * @class Roo.menu.ColorItem
15800  * @extends Roo.menu.Adapter
15801  * A menu item that wraps the {@link Roo.ColorPalette} component.
15802  * @constructor
15803  * Creates a new ColorItem
15804  * @param {Object} config Configuration options
15805  */
15806 Roo.menu.ColorItem = function(config){
15807     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15808     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15809     this.palette = this.component;
15810     this.relayEvents(this.palette, ["select"]);
15811     if(this.selectHandler){
15812         this.on('select', this.selectHandler, this.scope);
15813     }
15814 };
15815 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15816  * Based on:
15817  * Ext JS Library 1.1.1
15818  * Copyright(c) 2006-2007, Ext JS, LLC.
15819  *
15820  * Originally Released Under LGPL - original licence link has changed is not relivant.
15821  *
15822  * Fork - LGPL
15823  * <script type="text/javascript">
15824  */
15825  
15826
15827 /**
15828  * @class Roo.menu.DateMenu
15829  * @extends Roo.menu.Menu
15830  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15831  * @constructor
15832  * Creates a new DateMenu
15833  * @param {Object} config Configuration options
15834  */
15835 Roo.menu.DateMenu = function(config){
15836     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15837     this.plain = true;
15838     var di = new Roo.menu.DateItem(config);
15839     this.add(di);
15840     /**
15841      * The {@link Roo.DatePicker} instance for this DateMenu
15842      * @type DatePicker
15843      */
15844     this.picker = di.picker;
15845     /**
15846      * @event select
15847      * @param {DatePicker} picker
15848      * @param {Date} date
15849      */
15850     this.relayEvents(di, ["select"]);
15851     this.on('beforeshow', function(){
15852         if(this.picker){
15853             this.picker.hideMonthPicker(false);
15854         }
15855     }, this);
15856 };
15857 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15858     cls:'x-date-menu'
15859 });/*
15860  * Based on:
15861  * Ext JS Library 1.1.1
15862  * Copyright(c) 2006-2007, Ext JS, LLC.
15863  *
15864  * Originally Released Under LGPL - original licence link has changed is not relivant.
15865  *
15866  * Fork - LGPL
15867  * <script type="text/javascript">
15868  */
15869  
15870
15871 /**
15872  * @class Roo.menu.ColorMenu
15873  * @extends Roo.menu.Menu
15874  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15875  * @constructor
15876  * Creates a new ColorMenu
15877  * @param {Object} config Configuration options
15878  */
15879 Roo.menu.ColorMenu = function(config){
15880     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15881     this.plain = true;
15882     var ci = new Roo.menu.ColorItem(config);
15883     this.add(ci);
15884     /**
15885      * The {@link Roo.ColorPalette} instance for this ColorMenu
15886      * @type ColorPalette
15887      */
15888     this.palette = ci.palette;
15889     /**
15890      * @event select
15891      * @param {ColorPalette} palette
15892      * @param {String} color
15893      */
15894     this.relayEvents(ci, ["select"]);
15895 };
15896 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15897  * Based on:
15898  * Ext JS Library 1.1.1
15899  * Copyright(c) 2006-2007, Ext JS, LLC.
15900  *
15901  * Originally Released Under LGPL - original licence link has changed is not relivant.
15902  *
15903  * Fork - LGPL
15904  * <script type="text/javascript">
15905  */
15906  
15907 /**
15908  * @class Roo.form.TextItem
15909  * @extends Roo.BoxComponent
15910  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15911  * @constructor
15912  * Creates a new TextItem
15913  * @param {Object} config Configuration options
15914  */
15915 Roo.form.TextItem = function(config){
15916     Roo.form.TextItem.superclass.constructor.call(this, config);
15917 };
15918
15919 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15920     
15921     /**
15922      * @cfg {String} tag the tag for this item (default div)
15923      */
15924     tag : 'div',
15925     /**
15926      * @cfg {String} html the content for this item
15927      */
15928     html : '',
15929     
15930     getAutoCreate : function()
15931     {
15932         var cfg = {
15933             id: this.id,
15934             tag: this.tag,
15935             html: this.html,
15936             cls: 'x-form-item'
15937         };
15938         
15939         return cfg;
15940         
15941     },
15942     
15943     onRender : function(ct, position)
15944     {
15945         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15946         
15947         if(!this.el){
15948             var cfg = this.getAutoCreate();
15949             if(!cfg.name){
15950                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15951             }
15952             if (!cfg.name.length) {
15953                 delete cfg.name;
15954             }
15955             this.el = ct.createChild(cfg, position);
15956         }
15957     },
15958     /*
15959      * setHTML
15960      * @param {String} html update the Contents of the element.
15961      */
15962     setHTML : function(html)
15963     {
15964         this.fieldEl.dom.innerHTML = html;
15965     }
15966     
15967 });/*
15968  * Based on:
15969  * Ext JS Library 1.1.1
15970  * Copyright(c) 2006-2007, Ext JS, LLC.
15971  *
15972  * Originally Released Under LGPL - original licence link has changed is not relivant.
15973  *
15974  * Fork - LGPL
15975  * <script type="text/javascript">
15976  */
15977  
15978 /**
15979  * @class Roo.form.Field
15980  * @extends Roo.BoxComponent
15981  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15982  * @constructor
15983  * Creates a new Field
15984  * @param {Object} config Configuration options
15985  */
15986 Roo.form.Field = function(config){
15987     Roo.form.Field.superclass.constructor.call(this, config);
15988 };
15989
15990 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
15991     /**
15992      * @cfg {String} fieldLabel Label to use when rendering a form.
15993      */
15994        /**
15995      * @cfg {String} qtip Mouse over tip
15996      */
15997      
15998     /**
15999      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16000      */
16001     invalidClass : "x-form-invalid",
16002     /**
16003      * @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")
16004      */
16005     invalidText : "The value in this field is invalid",
16006     /**
16007      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16008      */
16009     focusClass : "x-form-focus",
16010     /**
16011      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16012       automatic validation (defaults to "keyup").
16013      */
16014     validationEvent : "keyup",
16015     /**
16016      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16017      */
16018     validateOnBlur : true,
16019     /**
16020      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16021      */
16022     validationDelay : 250,
16023     /**
16024      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16025      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16026      */
16027     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16028     /**
16029      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16030      */
16031     fieldClass : "x-form-field",
16032     /**
16033      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16034      *<pre>
16035 Value         Description
16036 -----------   ----------------------------------------------------------------------
16037 qtip          Display a quick tip when the user hovers over the field
16038 title         Display a default browser title attribute popup
16039 under         Add a block div beneath the field containing the error text
16040 side          Add an error icon to the right of the field with a popup on hover
16041 [element id]  Add the error text directly to the innerHTML of the specified element
16042 </pre>
16043      */
16044     msgTarget : 'qtip',
16045     /**
16046      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16047      */
16048     msgFx : 'normal',
16049
16050     /**
16051      * @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.
16052      */
16053     readOnly : false,
16054
16055     /**
16056      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16057      */
16058     disabled : false,
16059
16060     /**
16061      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16062      */
16063     inputType : undefined,
16064     
16065     /**
16066      * @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).
16067          */
16068         tabIndex : undefined,
16069         
16070     // private
16071     isFormField : true,
16072
16073     // private
16074     hasFocus : false,
16075     /**
16076      * @property {Roo.Element} fieldEl
16077      * Element Containing the rendered Field (with label etc.)
16078      */
16079     /**
16080      * @cfg {Mixed} value A value to initialize this field with.
16081      */
16082     value : undefined,
16083
16084     /**
16085      * @cfg {String} name The field's HTML name attribute.
16086      */
16087     /**
16088      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16089      */
16090     // private
16091     loadedValue : false,
16092      
16093      
16094         // private ??
16095         initComponent : function(){
16096         Roo.form.Field.superclass.initComponent.call(this);
16097         this.addEvents({
16098             /**
16099              * @event focus
16100              * Fires when this field receives input focus.
16101              * @param {Roo.form.Field} this
16102              */
16103             focus : true,
16104             /**
16105              * @event blur
16106              * Fires when this field loses input focus.
16107              * @param {Roo.form.Field} this
16108              */
16109             blur : true,
16110             /**
16111              * @event specialkey
16112              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16113              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16114              * @param {Roo.form.Field} this
16115              * @param {Roo.EventObject} e The event object
16116              */
16117             specialkey : true,
16118             /**
16119              * @event change
16120              * Fires just before the field blurs if the field value has changed.
16121              * @param {Roo.form.Field} this
16122              * @param {Mixed} newValue The new value
16123              * @param {Mixed} oldValue The original value
16124              */
16125             change : true,
16126             /**
16127              * @event invalid
16128              * Fires after the field has been marked as invalid.
16129              * @param {Roo.form.Field} this
16130              * @param {String} msg The validation message
16131              */
16132             invalid : true,
16133             /**
16134              * @event valid
16135              * Fires after the field has been validated with no errors.
16136              * @param {Roo.form.Field} this
16137              */
16138             valid : true,
16139              /**
16140              * @event keyup
16141              * Fires after the key up
16142              * @param {Roo.form.Field} this
16143              * @param {Roo.EventObject}  e The event Object
16144              */
16145             keyup : true
16146         });
16147     },
16148
16149     /**
16150      * Returns the name attribute of the field if available
16151      * @return {String} name The field name
16152      */
16153     getName: function(){
16154          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16155     },
16156
16157     // private
16158     onRender : function(ct, position){
16159         Roo.form.Field.superclass.onRender.call(this, ct, position);
16160         if(!this.el){
16161             var cfg = this.getAutoCreate();
16162             if(!cfg.name){
16163                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16164             }
16165             if (!cfg.name.length) {
16166                 delete cfg.name;
16167             }
16168             if(this.inputType){
16169                 cfg.type = this.inputType;
16170             }
16171             this.el = ct.createChild(cfg, position);
16172         }
16173         var type = this.el.dom.type;
16174         if(type){
16175             if(type == 'password'){
16176                 type = 'text';
16177             }
16178             this.el.addClass('x-form-'+type);
16179         }
16180         if(this.readOnly){
16181             this.el.dom.readOnly = true;
16182         }
16183         if(this.tabIndex !== undefined){
16184             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16185         }
16186
16187         this.el.addClass([this.fieldClass, this.cls]);
16188         this.initValue();
16189     },
16190
16191     /**
16192      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16193      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16194      * @return {Roo.form.Field} this
16195      */
16196     applyTo : function(target){
16197         this.allowDomMove = false;
16198         this.el = Roo.get(target);
16199         this.render(this.el.dom.parentNode);
16200         return this;
16201     },
16202
16203     // private
16204     initValue : function(){
16205         if(this.value !== undefined){
16206             this.setValue(this.value);
16207         }else if(this.el.dom.value.length > 0){
16208             this.setValue(this.el.dom.value);
16209         }
16210     },
16211
16212     /**
16213      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16214      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16215      */
16216     isDirty : function() {
16217         if(this.disabled) {
16218             return false;
16219         }
16220         return String(this.getValue()) !== String(this.originalValue);
16221     },
16222
16223     /**
16224      * stores the current value in loadedValue
16225      */
16226     resetHasChanged : function()
16227     {
16228         this.loadedValue = String(this.getValue());
16229     },
16230     /**
16231      * checks the current value against the 'loaded' value.
16232      * Note - will return false if 'resetHasChanged' has not been called first.
16233      */
16234     hasChanged : function()
16235     {
16236         if(this.disabled || this.readOnly) {
16237             return false;
16238         }
16239         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16240     },
16241     
16242     
16243     
16244     // private
16245     afterRender : function(){
16246         Roo.form.Field.superclass.afterRender.call(this);
16247         this.initEvents();
16248     },
16249
16250     // private
16251     fireKey : function(e){
16252         //Roo.log('field ' + e.getKey());
16253         if(e.isNavKeyPress()){
16254             this.fireEvent("specialkey", this, e);
16255         }
16256     },
16257
16258     /**
16259      * Resets the current field value to the originally loaded value and clears any validation messages
16260      */
16261     reset : function(){
16262         this.setValue(this.resetValue);
16263         this.originalValue = this.getValue();
16264         this.clearInvalid();
16265     },
16266
16267     // private
16268     initEvents : function(){
16269         // safari killled keypress - so keydown is now used..
16270         this.el.on("keydown" , this.fireKey,  this);
16271         this.el.on("focus", this.onFocus,  this);
16272         this.el.on("blur", this.onBlur,  this);
16273         this.el.relayEvent('keyup', this);
16274
16275         // reference to original value for reset
16276         this.originalValue = this.getValue();
16277         this.resetValue =  this.getValue();
16278     },
16279
16280     // private
16281     onFocus : function(){
16282         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16283             this.el.addClass(this.focusClass);
16284         }
16285         if(!this.hasFocus){
16286             this.hasFocus = true;
16287             this.startValue = this.getValue();
16288             this.fireEvent("focus", this);
16289         }
16290     },
16291
16292     beforeBlur : Roo.emptyFn,
16293
16294     // private
16295     onBlur : function(){
16296         this.beforeBlur();
16297         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16298             this.el.removeClass(this.focusClass);
16299         }
16300         this.hasFocus = false;
16301         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16302             this.validate();
16303         }
16304         var v = this.getValue();
16305         if(String(v) !== String(this.startValue)){
16306             this.fireEvent('change', this, v, this.startValue);
16307         }
16308         this.fireEvent("blur", this);
16309     },
16310
16311     /**
16312      * Returns whether or not the field value is currently valid
16313      * @param {Boolean} preventMark True to disable marking the field invalid
16314      * @return {Boolean} True if the value is valid, else false
16315      */
16316     isValid : function(preventMark){
16317         if(this.disabled){
16318             return true;
16319         }
16320         var restore = this.preventMark;
16321         this.preventMark = preventMark === true;
16322         var v = this.validateValue(this.processValue(this.getRawValue()));
16323         this.preventMark = restore;
16324         return v;
16325     },
16326
16327     /**
16328      * Validates the field value
16329      * @return {Boolean} True if the value is valid, else false
16330      */
16331     validate : function(){
16332         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16333             this.clearInvalid();
16334             return true;
16335         }
16336         return false;
16337     },
16338
16339     processValue : function(value){
16340         return value;
16341     },
16342
16343     // private
16344     // Subclasses should provide the validation implementation by overriding this
16345     validateValue : function(value){
16346         return true;
16347     },
16348
16349     /**
16350      * Mark this field as invalid
16351      * @param {String} msg The validation message
16352      */
16353     markInvalid : function(msg){
16354         if(!this.rendered || this.preventMark){ // not rendered
16355             return;
16356         }
16357         
16358         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16359         
16360         obj.el.addClass(this.invalidClass);
16361         msg = msg || this.invalidText;
16362         switch(this.msgTarget){
16363             case 'qtip':
16364                 obj.el.dom.qtip = msg;
16365                 obj.el.dom.qclass = 'x-form-invalid-tip';
16366                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16367                     Roo.QuickTips.enable();
16368                 }
16369                 break;
16370             case 'title':
16371                 this.el.dom.title = msg;
16372                 break;
16373             case 'under':
16374                 if(!this.errorEl){
16375                     var elp = this.el.findParent('.x-form-element', 5, true);
16376                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16377                     this.errorEl.setWidth(elp.getWidth(true)-20);
16378                 }
16379                 this.errorEl.update(msg);
16380                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16381                 break;
16382             case 'side':
16383                 if(!this.errorIcon){
16384                     var elp = this.el.findParent('.x-form-element', 5, true);
16385                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16386                 }
16387                 this.alignErrorIcon();
16388                 this.errorIcon.dom.qtip = msg;
16389                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16390                 this.errorIcon.show();
16391                 this.on('resize', this.alignErrorIcon, this);
16392                 break;
16393             default:
16394                 var t = Roo.getDom(this.msgTarget);
16395                 t.innerHTML = msg;
16396                 t.style.display = this.msgDisplay;
16397                 break;
16398         }
16399         this.fireEvent('invalid', this, msg);
16400     },
16401
16402     // private
16403     alignErrorIcon : function(){
16404         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16405     },
16406
16407     /**
16408      * Clear any invalid styles/messages for this field
16409      */
16410     clearInvalid : function(){
16411         if(!this.rendered || this.preventMark){ // not rendered
16412             return;
16413         }
16414         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16415         
16416         obj.el.removeClass(this.invalidClass);
16417         switch(this.msgTarget){
16418             case 'qtip':
16419                 obj.el.dom.qtip = '';
16420                 break;
16421             case 'title':
16422                 this.el.dom.title = '';
16423                 break;
16424             case 'under':
16425                 if(this.errorEl){
16426                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16427                 }
16428                 break;
16429             case 'side':
16430                 if(this.errorIcon){
16431                     this.errorIcon.dom.qtip = '';
16432                     this.errorIcon.hide();
16433                     this.un('resize', this.alignErrorIcon, this);
16434                 }
16435                 break;
16436             default:
16437                 var t = Roo.getDom(this.msgTarget);
16438                 t.innerHTML = '';
16439                 t.style.display = 'none';
16440                 break;
16441         }
16442         this.fireEvent('valid', this);
16443     },
16444
16445     /**
16446      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16447      * @return {Mixed} value The field value
16448      */
16449     getRawValue : function(){
16450         var v = this.el.getValue();
16451         
16452         return v;
16453     },
16454
16455     /**
16456      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16457      * @return {Mixed} value The field value
16458      */
16459     getValue : function(){
16460         var v = this.el.getValue();
16461          
16462         return v;
16463     },
16464
16465     /**
16466      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16467      * @param {Mixed} value The value to set
16468      */
16469     setRawValue : function(v){
16470         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16471     },
16472
16473     /**
16474      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16475      * @param {Mixed} value The value to set
16476      */
16477     setValue : function(v){
16478         this.value = v;
16479         if(this.rendered){
16480             this.el.dom.value = (v === null || v === undefined ? '' : v);
16481              this.validate();
16482         }
16483     },
16484
16485     adjustSize : function(w, h){
16486         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16487         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16488         return s;
16489     },
16490
16491     adjustWidth : function(tag, w){
16492         tag = tag.toLowerCase();
16493         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16494             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16495                 if(tag == 'input'){
16496                     return w + 2;
16497                 }
16498                 if(tag == 'textarea'){
16499                     return w-2;
16500                 }
16501             }else if(Roo.isOpera){
16502                 if(tag == 'input'){
16503                     return w + 2;
16504                 }
16505                 if(tag == 'textarea'){
16506                     return w-2;
16507                 }
16508             }
16509         }
16510         return w;
16511     }
16512 });
16513
16514
16515 // anything other than normal should be considered experimental
16516 Roo.form.Field.msgFx = {
16517     normal : {
16518         show: function(msgEl, f){
16519             msgEl.setDisplayed('block');
16520         },
16521
16522         hide : function(msgEl, f){
16523             msgEl.setDisplayed(false).update('');
16524         }
16525     },
16526
16527     slide : {
16528         show: function(msgEl, f){
16529             msgEl.slideIn('t', {stopFx:true});
16530         },
16531
16532         hide : function(msgEl, f){
16533             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16534         }
16535     },
16536
16537     slideRight : {
16538         show: function(msgEl, f){
16539             msgEl.fixDisplay();
16540             msgEl.alignTo(f.el, 'tl-tr');
16541             msgEl.slideIn('l', {stopFx:true});
16542         },
16543
16544         hide : function(msgEl, f){
16545             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16546         }
16547     }
16548 };/*
16549  * Based on:
16550  * Ext JS Library 1.1.1
16551  * Copyright(c) 2006-2007, Ext JS, LLC.
16552  *
16553  * Originally Released Under LGPL - original licence link has changed is not relivant.
16554  *
16555  * Fork - LGPL
16556  * <script type="text/javascript">
16557  */
16558  
16559
16560 /**
16561  * @class Roo.form.TextField
16562  * @extends Roo.form.Field
16563  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16564  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16565  * @constructor
16566  * Creates a new TextField
16567  * @param {Object} config Configuration options
16568  */
16569 Roo.form.TextField = function(config){
16570     Roo.form.TextField.superclass.constructor.call(this, config);
16571     this.addEvents({
16572         /**
16573          * @event autosize
16574          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16575          * according to the default logic, but this event provides a hook for the developer to apply additional
16576          * logic at runtime to resize the field if needed.
16577              * @param {Roo.form.Field} this This text field
16578              * @param {Number} width The new field width
16579              */
16580         autosize : true
16581     });
16582 };
16583
16584 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16585     /**
16586      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16587      */
16588     grow : false,
16589     /**
16590      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16591      */
16592     growMin : 30,
16593     /**
16594      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16595      */
16596     growMax : 800,
16597     /**
16598      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16599      */
16600     vtype : null,
16601     /**
16602      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16603      */
16604     maskRe : null,
16605     /**
16606      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16607      */
16608     disableKeyFilter : false,
16609     /**
16610      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16611      */
16612     allowBlank : true,
16613     /**
16614      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16615      */
16616     minLength : 0,
16617     /**
16618      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16619      */
16620     maxLength : Number.MAX_VALUE,
16621     /**
16622      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16623      */
16624     minLengthText : "The minimum length for this field is {0}",
16625     /**
16626      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16627      */
16628     maxLengthText : "The maximum length for this field is {0}",
16629     /**
16630      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16631      */
16632     selectOnFocus : false,
16633     /**
16634      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16635      */    
16636     allowLeadingSpace : false,
16637     /**
16638      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16639      */
16640     blankText : "This field is required",
16641     /**
16642      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16643      * If available, this function will be called only after the basic validators all return true, and will be passed the
16644      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16645      */
16646     validator : null,
16647     /**
16648      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16649      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16650      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16651      */
16652     regex : null,
16653     /**
16654      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16655      */
16656     regexText : "",
16657     /**
16658      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16659      */
16660     emptyText : null,
16661    
16662
16663     // private
16664     initEvents : function()
16665     {
16666         if (this.emptyText) {
16667             this.el.attr('placeholder', this.emptyText);
16668         }
16669         
16670         Roo.form.TextField.superclass.initEvents.call(this);
16671         if(this.validationEvent == 'keyup'){
16672             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16673             this.el.on('keyup', this.filterValidation, this);
16674         }
16675         else if(this.validationEvent !== false){
16676             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16677         }
16678         
16679         if(this.selectOnFocus){
16680             this.on("focus", this.preFocus, this);
16681         }
16682         if (!this.allowLeadingSpace) {
16683             this.on('blur', this.cleanLeadingSpace, this);
16684         }
16685         
16686         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16687             this.el.on("keypress", this.filterKeys, this);
16688         }
16689         if(this.grow){
16690             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16691             this.el.on("click", this.autoSize,  this);
16692         }
16693         if(this.el.is('input[type=password]') && Roo.isSafari){
16694             this.el.on('keydown', this.SafariOnKeyDown, this);
16695         }
16696     },
16697
16698     processValue : function(value){
16699         if(this.stripCharsRe){
16700             var newValue = value.replace(this.stripCharsRe, '');
16701             if(newValue !== value){
16702                 this.setRawValue(newValue);
16703                 return newValue;
16704             }
16705         }
16706         return value;
16707     },
16708
16709     filterValidation : function(e){
16710         if(!e.isNavKeyPress()){
16711             this.validationTask.delay(this.validationDelay);
16712         }
16713     },
16714
16715     // private
16716     onKeyUp : function(e){
16717         if(!e.isNavKeyPress()){
16718             this.autoSize();
16719         }
16720     },
16721     // private - clean the leading white space
16722     cleanLeadingSpace : function(e)
16723     {
16724         if ( this.inputType == 'file') {
16725             return;
16726         }
16727         
16728         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16729     },
16730     /**
16731      * Resets the current field value to the originally-loaded value and clears any validation messages.
16732      *  
16733      */
16734     reset : function(){
16735         Roo.form.TextField.superclass.reset.call(this);
16736        
16737     }, 
16738     // private
16739     preFocus : function(){
16740         
16741         if(this.selectOnFocus){
16742             this.el.dom.select();
16743         }
16744     },
16745
16746     
16747     // private
16748     filterKeys : function(e){
16749         var k = e.getKey();
16750         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16751             return;
16752         }
16753         var c = e.getCharCode(), cc = String.fromCharCode(c);
16754         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16755             return;
16756         }
16757         if(!this.maskRe.test(cc)){
16758             e.stopEvent();
16759         }
16760     },
16761
16762     setValue : function(v){
16763         
16764         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16765         
16766         this.autoSize();
16767     },
16768
16769     /**
16770      * Validates a value according to the field's validation rules and marks the field as invalid
16771      * if the validation fails
16772      * @param {Mixed} value The value to validate
16773      * @return {Boolean} True if the value is valid, else false
16774      */
16775     validateValue : function(value){
16776         if(value.length < 1)  { // if it's blank
16777              if(this.allowBlank){
16778                 this.clearInvalid();
16779                 return true;
16780              }else{
16781                 this.markInvalid(this.blankText);
16782                 return false;
16783              }
16784         }
16785         if(value.length < this.minLength){
16786             this.markInvalid(String.format(this.minLengthText, this.minLength));
16787             return false;
16788         }
16789         if(value.length > this.maxLength){
16790             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16791             return false;
16792         }
16793         if(this.vtype){
16794             var vt = Roo.form.VTypes;
16795             if(!vt[this.vtype](value, this)){
16796                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16797                 return false;
16798             }
16799         }
16800         if(typeof this.validator == "function"){
16801             var msg = this.validator(value);
16802             if(msg !== true){
16803                 this.markInvalid(msg);
16804                 return false;
16805             }
16806         }
16807         if(this.regex && !this.regex.test(value)){
16808             this.markInvalid(this.regexText);
16809             return false;
16810         }
16811         return true;
16812     },
16813
16814     /**
16815      * Selects text in this field
16816      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16817      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16818      */
16819     selectText : function(start, end){
16820         var v = this.getRawValue();
16821         if(v.length > 0){
16822             start = start === undefined ? 0 : start;
16823             end = end === undefined ? v.length : end;
16824             var d = this.el.dom;
16825             if(d.setSelectionRange){
16826                 d.setSelectionRange(start, end);
16827             }else if(d.createTextRange){
16828                 var range = d.createTextRange();
16829                 range.moveStart("character", start);
16830                 range.moveEnd("character", v.length-end);
16831                 range.select();
16832             }
16833         }
16834     },
16835
16836     /**
16837      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16838      * This only takes effect if grow = true, and fires the autosize event.
16839      */
16840     autoSize : function(){
16841         if(!this.grow || !this.rendered){
16842             return;
16843         }
16844         if(!this.metrics){
16845             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16846         }
16847         var el = this.el;
16848         var v = el.dom.value;
16849         var d = document.createElement('div');
16850         d.appendChild(document.createTextNode(v));
16851         v = d.innerHTML;
16852         d = null;
16853         v += "&#160;";
16854         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16855         this.el.setWidth(w);
16856         this.fireEvent("autosize", this, w);
16857     },
16858     
16859     // private
16860     SafariOnKeyDown : function(event)
16861     {
16862         // this is a workaround for a password hang bug on chrome/ webkit.
16863         
16864         var isSelectAll = false;
16865         
16866         if(this.el.dom.selectionEnd > 0){
16867             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16868         }
16869         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16870             event.preventDefault();
16871             this.setValue('');
16872             return;
16873         }
16874         
16875         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16876             
16877             event.preventDefault();
16878             // this is very hacky as keydown always get's upper case.
16879             
16880             var cc = String.fromCharCode(event.getCharCode());
16881             
16882             
16883             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16884             
16885         }
16886         
16887         
16888     }
16889 });/*
16890  * Based on:
16891  * Ext JS Library 1.1.1
16892  * Copyright(c) 2006-2007, Ext JS, LLC.
16893  *
16894  * Originally Released Under LGPL - original licence link has changed is not relivant.
16895  *
16896  * Fork - LGPL
16897  * <script type="text/javascript">
16898  */
16899  
16900 /**
16901  * @class Roo.form.Hidden
16902  * @extends Roo.form.TextField
16903  * Simple Hidden element used on forms 
16904  * 
16905  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16906  * 
16907  * @constructor
16908  * Creates a new Hidden form element.
16909  * @param {Object} config Configuration options
16910  */
16911
16912
16913
16914 // easy hidden field...
16915 Roo.form.Hidden = function(config){
16916     Roo.form.Hidden.superclass.constructor.call(this, config);
16917 };
16918   
16919 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16920     fieldLabel:      '',
16921     inputType:      'hidden',
16922     width:          50,
16923     allowBlank:     true,
16924     labelSeparator: '',
16925     hidden:         true,
16926     itemCls :       'x-form-item-display-none'
16927
16928
16929 });
16930
16931
16932 /*
16933  * Based on:
16934  * Ext JS Library 1.1.1
16935  * Copyright(c) 2006-2007, Ext JS, LLC.
16936  *
16937  * Originally Released Under LGPL - original licence link has changed is not relivant.
16938  *
16939  * Fork - LGPL
16940  * <script type="text/javascript">
16941  */
16942  
16943 /**
16944  * @class Roo.form.TriggerField
16945  * @extends Roo.form.TextField
16946  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16947  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16948  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16949  * for which you can provide a custom implementation.  For example:
16950  * <pre><code>
16951 var trigger = new Roo.form.TriggerField();
16952 trigger.onTriggerClick = myTriggerFn;
16953 trigger.applyTo('my-field');
16954 </code></pre>
16955  *
16956  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16957  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16958  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
16959  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16960  * @constructor
16961  * Create a new TriggerField.
16962  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16963  * to the base TextField)
16964  */
16965 Roo.form.TriggerField = function(config){
16966     this.mimicing = false;
16967     Roo.form.TriggerField.superclass.constructor.call(this, config);
16968 };
16969
16970 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
16971     /**
16972      * @cfg {String} triggerClass A CSS class to apply to the trigger
16973      */
16974     /**
16975      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16976      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
16977      */
16978     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
16979     /**
16980      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
16981      */
16982     hideTrigger:false,
16983
16984     /** @cfg {Boolean} grow @hide */
16985     /** @cfg {Number} growMin @hide */
16986     /** @cfg {Number} growMax @hide */
16987
16988     /**
16989      * @hide 
16990      * @method
16991      */
16992     autoSize: Roo.emptyFn,
16993     // private
16994     monitorTab : true,
16995     // private
16996     deferHeight : true,
16997
16998     
16999     actionMode : 'wrap',
17000     // private
17001     onResize : function(w, h){
17002         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17003         if(typeof w == 'number'){
17004             var x = w - this.trigger.getWidth();
17005             this.el.setWidth(this.adjustWidth('input', x));
17006             this.trigger.setStyle('left', x+'px');
17007         }
17008     },
17009
17010     // private
17011     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17012
17013     // private
17014     getResizeEl : function(){
17015         return this.wrap;
17016     },
17017
17018     // private
17019     getPositionEl : function(){
17020         return this.wrap;
17021     },
17022
17023     // private
17024     alignErrorIcon : function(){
17025         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17026     },
17027
17028     // private
17029     onRender : function(ct, position){
17030         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17031         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17032         this.trigger = this.wrap.createChild(this.triggerConfig ||
17033                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17034         if(this.hideTrigger){
17035             this.trigger.setDisplayed(false);
17036         }
17037         this.initTrigger();
17038         if(!this.width){
17039             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17040         }
17041     },
17042
17043     // private
17044     initTrigger : function(){
17045         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17046         this.trigger.addClassOnOver('x-form-trigger-over');
17047         this.trigger.addClassOnClick('x-form-trigger-click');
17048     },
17049
17050     // private
17051     onDestroy : function(){
17052         if(this.trigger){
17053             this.trigger.removeAllListeners();
17054             this.trigger.remove();
17055         }
17056         if(this.wrap){
17057             this.wrap.remove();
17058         }
17059         Roo.form.TriggerField.superclass.onDestroy.call(this);
17060     },
17061
17062     // private
17063     onFocus : function(){
17064         Roo.form.TriggerField.superclass.onFocus.call(this);
17065         if(!this.mimicing){
17066             this.wrap.addClass('x-trigger-wrap-focus');
17067             this.mimicing = true;
17068             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17069             if(this.monitorTab){
17070                 this.el.on("keydown", this.checkTab, this);
17071             }
17072         }
17073     },
17074
17075     // private
17076     checkTab : function(e){
17077         if(e.getKey() == e.TAB){
17078             this.triggerBlur();
17079         }
17080     },
17081
17082     // private
17083     onBlur : function(){
17084         // do nothing
17085     },
17086
17087     // private
17088     mimicBlur : function(e, t){
17089         if(!this.wrap.contains(t) && this.validateBlur()){
17090             this.triggerBlur();
17091         }
17092     },
17093
17094     // private
17095     triggerBlur : function(){
17096         this.mimicing = false;
17097         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17098         if(this.monitorTab){
17099             this.el.un("keydown", this.checkTab, this);
17100         }
17101         this.wrap.removeClass('x-trigger-wrap-focus');
17102         Roo.form.TriggerField.superclass.onBlur.call(this);
17103     },
17104
17105     // private
17106     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17107     validateBlur : function(e, t){
17108         return true;
17109     },
17110
17111     // private
17112     onDisable : function(){
17113         Roo.form.TriggerField.superclass.onDisable.call(this);
17114         if(this.wrap){
17115             this.wrap.addClass('x-item-disabled');
17116         }
17117     },
17118
17119     // private
17120     onEnable : function(){
17121         Roo.form.TriggerField.superclass.onEnable.call(this);
17122         if(this.wrap){
17123             this.wrap.removeClass('x-item-disabled');
17124         }
17125     },
17126
17127     // private
17128     onShow : function(){
17129         var ae = this.getActionEl();
17130         
17131         if(ae){
17132             ae.dom.style.display = '';
17133             ae.dom.style.visibility = 'visible';
17134         }
17135     },
17136
17137     // private
17138     
17139     onHide : function(){
17140         var ae = this.getActionEl();
17141         ae.dom.style.display = 'none';
17142     },
17143
17144     /**
17145      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17146      * by an implementing function.
17147      * @method
17148      * @param {EventObject} e
17149      */
17150     onTriggerClick : Roo.emptyFn
17151 });
17152
17153 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17154 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17155 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17156 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17157     initComponent : function(){
17158         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17159
17160         this.triggerConfig = {
17161             tag:'span', cls:'x-form-twin-triggers', cn:[
17162             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17163             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17164         ]};
17165     },
17166
17167     getTrigger : function(index){
17168         return this.triggers[index];
17169     },
17170
17171     initTrigger : function(){
17172         var ts = this.trigger.select('.x-form-trigger', true);
17173         this.wrap.setStyle('overflow', 'hidden');
17174         var triggerField = this;
17175         ts.each(function(t, all, index){
17176             t.hide = function(){
17177                 var w = triggerField.wrap.getWidth();
17178                 this.dom.style.display = 'none';
17179                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17180             };
17181             t.show = function(){
17182                 var w = triggerField.wrap.getWidth();
17183                 this.dom.style.display = '';
17184                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17185             };
17186             var triggerIndex = 'Trigger'+(index+1);
17187
17188             if(this['hide'+triggerIndex]){
17189                 t.dom.style.display = 'none';
17190             }
17191             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17192             t.addClassOnOver('x-form-trigger-over');
17193             t.addClassOnClick('x-form-trigger-click');
17194         }, this);
17195         this.triggers = ts.elements;
17196     },
17197
17198     onTrigger1Click : Roo.emptyFn,
17199     onTrigger2Click : Roo.emptyFn
17200 });/*
17201  * Based on:
17202  * Ext JS Library 1.1.1
17203  * Copyright(c) 2006-2007, Ext JS, LLC.
17204  *
17205  * Originally Released Under LGPL - original licence link has changed is not relivant.
17206  *
17207  * Fork - LGPL
17208  * <script type="text/javascript">
17209  */
17210  
17211 /**
17212  * @class Roo.form.TextArea
17213  * @extends Roo.form.TextField
17214  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17215  * support for auto-sizing.
17216  * @constructor
17217  * Creates a new TextArea
17218  * @param {Object} config Configuration options
17219  */
17220 Roo.form.TextArea = function(config){
17221     Roo.form.TextArea.superclass.constructor.call(this, config);
17222     // these are provided exchanges for backwards compat
17223     // minHeight/maxHeight were replaced by growMin/growMax to be
17224     // compatible with TextField growing config values
17225     if(this.minHeight !== undefined){
17226         this.growMin = this.minHeight;
17227     }
17228     if(this.maxHeight !== undefined){
17229         this.growMax = this.maxHeight;
17230     }
17231 };
17232
17233 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17234     /**
17235      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17236      */
17237     growMin : 60,
17238     /**
17239      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17240      */
17241     growMax: 1000,
17242     /**
17243      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17244      * in the field (equivalent to setting overflow: hidden, defaults to false)
17245      */
17246     preventScrollbars: false,
17247     /**
17248      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17249      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17250      */
17251
17252     // private
17253     onRender : function(ct, position){
17254         if(!this.el){
17255             this.defaultAutoCreate = {
17256                 tag: "textarea",
17257                 style:"width:300px;height:60px;",
17258                 autocomplete: "new-password"
17259             };
17260         }
17261         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17262         if(this.grow){
17263             this.textSizeEl = Roo.DomHelper.append(document.body, {
17264                 tag: "pre", cls: "x-form-grow-sizer"
17265             });
17266             if(this.preventScrollbars){
17267                 this.el.setStyle("overflow", "hidden");
17268             }
17269             this.el.setHeight(this.growMin);
17270         }
17271     },
17272
17273     onDestroy : function(){
17274         if(this.textSizeEl){
17275             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17276         }
17277         Roo.form.TextArea.superclass.onDestroy.call(this);
17278     },
17279
17280     // private
17281     onKeyUp : function(e){
17282         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17283             this.autoSize();
17284         }
17285     },
17286
17287     /**
17288      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17289      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17290      */
17291     autoSize : function(){
17292         if(!this.grow || !this.textSizeEl){
17293             return;
17294         }
17295         var el = this.el;
17296         var v = el.dom.value;
17297         var ts = this.textSizeEl;
17298
17299         ts.innerHTML = '';
17300         ts.appendChild(document.createTextNode(v));
17301         v = ts.innerHTML;
17302
17303         Roo.fly(ts).setWidth(this.el.getWidth());
17304         if(v.length < 1){
17305             v = "&#160;&#160;";
17306         }else{
17307             if(Roo.isIE){
17308                 v = v.replace(/\n/g, '<p>&#160;</p>');
17309             }
17310             v += "&#160;\n&#160;";
17311         }
17312         ts.innerHTML = v;
17313         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17314         if(h != this.lastHeight){
17315             this.lastHeight = h;
17316             this.el.setHeight(h);
17317             this.fireEvent("autosize", this, h);
17318         }
17319     }
17320 });/*
17321  * Based on:
17322  * Ext JS Library 1.1.1
17323  * Copyright(c) 2006-2007, Ext JS, LLC.
17324  *
17325  * Originally Released Under LGPL - original licence link has changed is not relivant.
17326  *
17327  * Fork - LGPL
17328  * <script type="text/javascript">
17329  */
17330  
17331
17332 /**
17333  * @class Roo.form.NumberField
17334  * @extends Roo.form.TextField
17335  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17336  * @constructor
17337  * Creates a new NumberField
17338  * @param {Object} config Configuration options
17339  */
17340 Roo.form.NumberField = function(config){
17341     Roo.form.NumberField.superclass.constructor.call(this, config);
17342 };
17343
17344 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17345     /**
17346      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17347      */
17348     fieldClass: "x-form-field x-form-num-field",
17349     /**
17350      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17351      */
17352     allowDecimals : true,
17353     /**
17354      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17355      */
17356     decimalSeparator : ".",
17357     /**
17358      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17359      */
17360     decimalPrecision : 2,
17361     /**
17362      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17363      */
17364     allowNegative : true,
17365     /**
17366      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17367      */
17368     minValue : Number.NEGATIVE_INFINITY,
17369     /**
17370      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17371      */
17372     maxValue : Number.MAX_VALUE,
17373     /**
17374      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17375      */
17376     minText : "The minimum value for this field is {0}",
17377     /**
17378      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17379      */
17380     maxText : "The maximum value for this field is {0}",
17381     /**
17382      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17383      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17384      */
17385     nanText : "{0} is not a valid number",
17386
17387     // private
17388     initEvents : function(){
17389         Roo.form.NumberField.superclass.initEvents.call(this);
17390         var allowed = "0123456789";
17391         if(this.allowDecimals){
17392             allowed += this.decimalSeparator;
17393         }
17394         if(this.allowNegative){
17395             allowed += "-";
17396         }
17397         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17398         var keyPress = function(e){
17399             var k = e.getKey();
17400             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17401                 return;
17402             }
17403             var c = e.getCharCode();
17404             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17405                 e.stopEvent();
17406             }
17407         };
17408         this.el.on("keypress", keyPress, this);
17409     },
17410
17411     // private
17412     validateValue : function(value){
17413         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17414             return false;
17415         }
17416         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17417              return true;
17418         }
17419         var num = this.parseValue(value);
17420         if(isNaN(num)){
17421             this.markInvalid(String.format(this.nanText, value));
17422             return false;
17423         }
17424         if(num < this.minValue){
17425             this.markInvalid(String.format(this.minText, this.minValue));
17426             return false;
17427         }
17428         if(num > this.maxValue){
17429             this.markInvalid(String.format(this.maxText, this.maxValue));
17430             return false;
17431         }
17432         return true;
17433     },
17434
17435     getValue : function(){
17436         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17437     },
17438
17439     // private
17440     parseValue : function(value){
17441         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17442         return isNaN(value) ? '' : value;
17443     },
17444
17445     // private
17446     fixPrecision : function(value){
17447         var nan = isNaN(value);
17448         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17449             return nan ? '' : value;
17450         }
17451         return parseFloat(value).toFixed(this.decimalPrecision);
17452     },
17453
17454     setValue : function(v){
17455         v = this.fixPrecision(v);
17456         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17457     },
17458
17459     // private
17460     decimalPrecisionFcn : function(v){
17461         return Math.floor(v);
17462     },
17463
17464     beforeBlur : function(){
17465         var v = this.parseValue(this.getRawValue());
17466         if(v){
17467             this.setValue(v);
17468         }
17469     }
17470 });/*
17471  * Based on:
17472  * Ext JS Library 1.1.1
17473  * Copyright(c) 2006-2007, Ext JS, LLC.
17474  *
17475  * Originally Released Under LGPL - original licence link has changed is not relivant.
17476  *
17477  * Fork - LGPL
17478  * <script type="text/javascript">
17479  */
17480  
17481 /**
17482  * @class Roo.form.DateField
17483  * @extends Roo.form.TriggerField
17484  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17485 * @constructor
17486 * Create a new DateField
17487 * @param {Object} config
17488  */
17489 Roo.form.DateField = function(config)
17490 {
17491     Roo.form.DateField.superclass.constructor.call(this, config);
17492     
17493       this.addEvents({
17494          
17495         /**
17496          * @event select
17497          * Fires when a date is selected
17498              * @param {Roo.form.DateField} combo This combo box
17499              * @param {Date} date The date selected
17500              */
17501         'select' : true
17502          
17503     });
17504     
17505     
17506     if(typeof this.minValue == "string") {
17507         this.minValue = this.parseDate(this.minValue);
17508     }
17509     if(typeof this.maxValue == "string") {
17510         this.maxValue = this.parseDate(this.maxValue);
17511     }
17512     this.ddMatch = null;
17513     if(this.disabledDates){
17514         var dd = this.disabledDates;
17515         var re = "(?:";
17516         for(var i = 0; i < dd.length; i++){
17517             re += dd[i];
17518             if(i != dd.length-1) {
17519                 re += "|";
17520             }
17521         }
17522         this.ddMatch = new RegExp(re + ")");
17523     }
17524 };
17525
17526 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17527     /**
17528      * @cfg {String} format
17529      * The default date format string which can be overriden for localization support.  The format must be
17530      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17531      */
17532     format : "m/d/y",
17533     /**
17534      * @cfg {String} altFormats
17535      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17536      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17537      */
17538     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17539     /**
17540      * @cfg {Array} disabledDays
17541      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17542      */
17543     disabledDays : null,
17544     /**
17545      * @cfg {String} disabledDaysText
17546      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17547      */
17548     disabledDaysText : "Disabled",
17549     /**
17550      * @cfg {Array} disabledDates
17551      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17552      * expression so they are very powerful. Some examples:
17553      * <ul>
17554      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17555      * <li>["03/08", "09/16"] would disable those days for every year</li>
17556      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17557      * <li>["03/../2006"] would disable every day in March 2006</li>
17558      * <li>["^03"] would disable every day in every March</li>
17559      * </ul>
17560      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17561      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17562      */
17563     disabledDates : null,
17564     /**
17565      * @cfg {String} disabledDatesText
17566      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17567      */
17568     disabledDatesText : "Disabled",
17569     /**
17570      * @cfg {Date/String} minValue
17571      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17572      * valid format (defaults to null).
17573      */
17574     minValue : null,
17575     /**
17576      * @cfg {Date/String} maxValue
17577      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17578      * valid format (defaults to null).
17579      */
17580     maxValue : null,
17581     /**
17582      * @cfg {String} minText
17583      * The error text to display when the date in the cell is before minValue (defaults to
17584      * 'The date in this field must be after {minValue}').
17585      */
17586     minText : "The date in this field must be equal to or after {0}",
17587     /**
17588      * @cfg {String} maxText
17589      * The error text to display when the date in the cell is after maxValue (defaults to
17590      * 'The date in this field must be before {maxValue}').
17591      */
17592     maxText : "The date in this field must be equal to or before {0}",
17593     /**
17594      * @cfg {String} invalidText
17595      * The error text to display when the date in the field is invalid (defaults to
17596      * '{value} is not a valid date - it must be in the format {format}').
17597      */
17598     invalidText : "{0} is not a valid date - it must be in the format {1}",
17599     /**
17600      * @cfg {String} triggerClass
17601      * An additional CSS class used to style the trigger button.  The trigger will always get the
17602      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17603      * which displays a calendar icon).
17604      */
17605     triggerClass : 'x-form-date-trigger',
17606     
17607
17608     /**
17609      * @cfg {Boolean} useIso
17610      * if enabled, then the date field will use a hidden field to store the 
17611      * real value as iso formated date. default (false)
17612      */ 
17613     useIso : false,
17614     /**
17615      * @cfg {String/Object} autoCreate
17616      * A DomHelper element spec, or true for a default element spec (defaults to
17617      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17618      */ 
17619     // private
17620     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17621     
17622     // private
17623     hiddenField: false,
17624     
17625     onRender : function(ct, position)
17626     {
17627         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17628         if (this.useIso) {
17629             //this.el.dom.removeAttribute('name'); 
17630             Roo.log("Changing name?");
17631             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17632             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17633                     'before', true);
17634             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17635             // prevent input submission
17636             this.hiddenName = this.name;
17637         }
17638             
17639             
17640     },
17641     
17642     // private
17643     validateValue : function(value)
17644     {
17645         value = this.formatDate(value);
17646         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17647             Roo.log('super failed');
17648             return false;
17649         }
17650         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17651              return true;
17652         }
17653         var svalue = value;
17654         value = this.parseDate(value);
17655         if(!value){
17656             Roo.log('parse date failed' + svalue);
17657             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17658             return false;
17659         }
17660         var time = value.getTime();
17661         if(this.minValue && time < this.minValue.getTime()){
17662             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17663             return false;
17664         }
17665         if(this.maxValue && time > this.maxValue.getTime()){
17666             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17667             return false;
17668         }
17669         if(this.disabledDays){
17670             var day = value.getDay();
17671             for(var i = 0; i < this.disabledDays.length; i++) {
17672                 if(day === this.disabledDays[i]){
17673                     this.markInvalid(this.disabledDaysText);
17674                     return false;
17675                 }
17676             }
17677         }
17678         var fvalue = this.formatDate(value);
17679         if(this.ddMatch && this.ddMatch.test(fvalue)){
17680             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17681             return false;
17682         }
17683         return true;
17684     },
17685
17686     // private
17687     // Provides logic to override the default TriggerField.validateBlur which just returns true
17688     validateBlur : function(){
17689         return !this.menu || !this.menu.isVisible();
17690     },
17691     
17692     getName: function()
17693     {
17694         // returns hidden if it's set..
17695         if (!this.rendered) {return ''};
17696         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17697         
17698     },
17699
17700     /**
17701      * Returns the current date value of the date field.
17702      * @return {Date} The date value
17703      */
17704     getValue : function(){
17705         
17706         return  this.hiddenField ?
17707                 this.hiddenField.value :
17708                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17709     },
17710
17711     /**
17712      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17713      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17714      * (the default format used is "m/d/y").
17715      * <br />Usage:
17716      * <pre><code>
17717 //All of these calls set the same date value (May 4, 2006)
17718
17719 //Pass a date object:
17720 var dt = new Date('5/4/06');
17721 dateField.setValue(dt);
17722
17723 //Pass a date string (default format):
17724 dateField.setValue('5/4/06');
17725
17726 //Pass a date string (custom format):
17727 dateField.format = 'Y-m-d';
17728 dateField.setValue('2006-5-4');
17729 </code></pre>
17730      * @param {String/Date} date The date or valid date string
17731      */
17732     setValue : function(date){
17733         if (this.hiddenField) {
17734             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17735         }
17736         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17737         // make sure the value field is always stored as a date..
17738         this.value = this.parseDate(date);
17739         
17740         
17741     },
17742
17743     // private
17744     parseDate : function(value){
17745         if(!value || value instanceof Date){
17746             return value;
17747         }
17748         var v = Date.parseDate(value, this.format);
17749          if (!v && this.useIso) {
17750             v = Date.parseDate(value, 'Y-m-d');
17751         }
17752         if(!v && this.altFormats){
17753             if(!this.altFormatsArray){
17754                 this.altFormatsArray = this.altFormats.split("|");
17755             }
17756             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17757                 v = Date.parseDate(value, this.altFormatsArray[i]);
17758             }
17759         }
17760         return v;
17761     },
17762
17763     // private
17764     formatDate : function(date, fmt){
17765         return (!date || !(date instanceof Date)) ?
17766                date : date.dateFormat(fmt || this.format);
17767     },
17768
17769     // private
17770     menuListeners : {
17771         select: function(m, d){
17772             
17773             this.setValue(d);
17774             this.fireEvent('select', this, d);
17775         },
17776         show : function(){ // retain focus styling
17777             this.onFocus();
17778         },
17779         hide : function(){
17780             this.focus.defer(10, this);
17781             var ml = this.menuListeners;
17782             this.menu.un("select", ml.select,  this);
17783             this.menu.un("show", ml.show,  this);
17784             this.menu.un("hide", ml.hide,  this);
17785         }
17786     },
17787
17788     // private
17789     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17790     onTriggerClick : function(){
17791         if(this.disabled){
17792             return;
17793         }
17794         if(this.menu == null){
17795             this.menu = new Roo.menu.DateMenu();
17796         }
17797         Roo.apply(this.menu.picker,  {
17798             showClear: this.allowBlank,
17799             minDate : this.minValue,
17800             maxDate : this.maxValue,
17801             disabledDatesRE : this.ddMatch,
17802             disabledDatesText : this.disabledDatesText,
17803             disabledDays : this.disabledDays,
17804             disabledDaysText : this.disabledDaysText,
17805             format : this.useIso ? 'Y-m-d' : this.format,
17806             minText : String.format(this.minText, this.formatDate(this.minValue)),
17807             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17808         });
17809         this.menu.on(Roo.apply({}, this.menuListeners, {
17810             scope:this
17811         }));
17812         this.menu.picker.setValue(this.getValue() || new Date());
17813         this.menu.show(this.el, "tl-bl?");
17814     },
17815
17816     beforeBlur : function(){
17817         var v = this.parseDate(this.getRawValue());
17818         if(v){
17819             this.setValue(v);
17820         }
17821     },
17822
17823     /*@
17824      * overide
17825      * 
17826      */
17827     isDirty : function() {
17828         if(this.disabled) {
17829             return false;
17830         }
17831         
17832         if(typeof(this.startValue) === 'undefined'){
17833             return false;
17834         }
17835         
17836         return String(this.getValue()) !== String(this.startValue);
17837         
17838     },
17839     // @overide
17840     cleanLeadingSpace : function(e)
17841     {
17842        return;
17843     }
17844     
17845 });/*
17846  * Based on:
17847  * Ext JS Library 1.1.1
17848  * Copyright(c) 2006-2007, Ext JS, LLC.
17849  *
17850  * Originally Released Under LGPL - original licence link has changed is not relivant.
17851  *
17852  * Fork - LGPL
17853  * <script type="text/javascript">
17854  */
17855  
17856 /**
17857  * @class Roo.form.MonthField
17858  * @extends Roo.form.TriggerField
17859  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17860 * @constructor
17861 * Create a new MonthField
17862 * @param {Object} config
17863  */
17864 Roo.form.MonthField = function(config){
17865     
17866     Roo.form.MonthField.superclass.constructor.call(this, config);
17867     
17868       this.addEvents({
17869          
17870         /**
17871          * @event select
17872          * Fires when a date is selected
17873              * @param {Roo.form.MonthFieeld} combo This combo box
17874              * @param {Date} date The date selected
17875              */
17876         'select' : true
17877          
17878     });
17879     
17880     
17881     if(typeof this.minValue == "string") {
17882         this.minValue = this.parseDate(this.minValue);
17883     }
17884     if(typeof this.maxValue == "string") {
17885         this.maxValue = this.parseDate(this.maxValue);
17886     }
17887     this.ddMatch = null;
17888     if(this.disabledDates){
17889         var dd = this.disabledDates;
17890         var re = "(?:";
17891         for(var i = 0; i < dd.length; i++){
17892             re += dd[i];
17893             if(i != dd.length-1) {
17894                 re += "|";
17895             }
17896         }
17897         this.ddMatch = new RegExp(re + ")");
17898     }
17899 };
17900
17901 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17902     /**
17903      * @cfg {String} format
17904      * The default date format string which can be overriden for localization support.  The format must be
17905      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17906      */
17907     format : "M Y",
17908     /**
17909      * @cfg {String} altFormats
17910      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17911      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17912      */
17913     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17914     /**
17915      * @cfg {Array} disabledDays
17916      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17917      */
17918     disabledDays : [0,1,2,3,4,5,6],
17919     /**
17920      * @cfg {String} disabledDaysText
17921      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17922      */
17923     disabledDaysText : "Disabled",
17924     /**
17925      * @cfg {Array} disabledDates
17926      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17927      * expression so they are very powerful. Some examples:
17928      * <ul>
17929      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17930      * <li>["03/08", "09/16"] would disable those days for every year</li>
17931      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17932      * <li>["03/../2006"] would disable every day in March 2006</li>
17933      * <li>["^03"] would disable every day in every March</li>
17934      * </ul>
17935      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17936      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17937      */
17938     disabledDates : null,
17939     /**
17940      * @cfg {String} disabledDatesText
17941      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17942      */
17943     disabledDatesText : "Disabled",
17944     /**
17945      * @cfg {Date/String} minValue
17946      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17947      * valid format (defaults to null).
17948      */
17949     minValue : null,
17950     /**
17951      * @cfg {Date/String} maxValue
17952      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17953      * valid format (defaults to null).
17954      */
17955     maxValue : null,
17956     /**
17957      * @cfg {String} minText
17958      * The error text to display when the date in the cell is before minValue (defaults to
17959      * 'The date in this field must be after {minValue}').
17960      */
17961     minText : "The date in this field must be equal to or after {0}",
17962     /**
17963      * @cfg {String} maxTextf
17964      * The error text to display when the date in the cell is after maxValue (defaults to
17965      * 'The date in this field must be before {maxValue}').
17966      */
17967     maxText : "The date in this field must be equal to or before {0}",
17968     /**
17969      * @cfg {String} invalidText
17970      * The error text to display when the date in the field is invalid (defaults to
17971      * '{value} is not a valid date - it must be in the format {format}').
17972      */
17973     invalidText : "{0} is not a valid date - it must be in the format {1}",
17974     /**
17975      * @cfg {String} triggerClass
17976      * An additional CSS class used to style the trigger button.  The trigger will always get the
17977      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17978      * which displays a calendar icon).
17979      */
17980     triggerClass : 'x-form-date-trigger',
17981     
17982
17983     /**
17984      * @cfg {Boolean} useIso
17985      * if enabled, then the date field will use a hidden field to store the 
17986      * real value as iso formated date. default (true)
17987      */ 
17988     useIso : true,
17989     /**
17990      * @cfg {String/Object} autoCreate
17991      * A DomHelper element spec, or true for a default element spec (defaults to
17992      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17993      */ 
17994     // private
17995     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
17996     
17997     // private
17998     hiddenField: false,
17999     
18000     hideMonthPicker : false,
18001     
18002     onRender : function(ct, position)
18003     {
18004         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18005         if (this.useIso) {
18006             this.el.dom.removeAttribute('name'); 
18007             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18008                     'before', true);
18009             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18010             // prevent input submission
18011             this.hiddenName = this.name;
18012         }
18013             
18014             
18015     },
18016     
18017     // private
18018     validateValue : function(value)
18019     {
18020         value = this.formatDate(value);
18021         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18022             return false;
18023         }
18024         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18025              return true;
18026         }
18027         var svalue = value;
18028         value = this.parseDate(value);
18029         if(!value){
18030             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18031             return false;
18032         }
18033         var time = value.getTime();
18034         if(this.minValue && time < this.minValue.getTime()){
18035             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18036             return false;
18037         }
18038         if(this.maxValue && time > this.maxValue.getTime()){
18039             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18040             return false;
18041         }
18042         /*if(this.disabledDays){
18043             var day = value.getDay();
18044             for(var i = 0; i < this.disabledDays.length; i++) {
18045                 if(day === this.disabledDays[i]){
18046                     this.markInvalid(this.disabledDaysText);
18047                     return false;
18048                 }
18049             }
18050         }
18051         */
18052         var fvalue = this.formatDate(value);
18053         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18054             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18055             return false;
18056         }
18057         */
18058         return true;
18059     },
18060
18061     // private
18062     // Provides logic to override the default TriggerField.validateBlur which just returns true
18063     validateBlur : function(){
18064         return !this.menu || !this.menu.isVisible();
18065     },
18066
18067     /**
18068      * Returns the current date value of the date field.
18069      * @return {Date} The date value
18070      */
18071     getValue : function(){
18072         
18073         
18074         
18075         return  this.hiddenField ?
18076                 this.hiddenField.value :
18077                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18078     },
18079
18080     /**
18081      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18082      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18083      * (the default format used is "m/d/y").
18084      * <br />Usage:
18085      * <pre><code>
18086 //All of these calls set the same date value (May 4, 2006)
18087
18088 //Pass a date object:
18089 var dt = new Date('5/4/06');
18090 monthField.setValue(dt);
18091
18092 //Pass a date string (default format):
18093 monthField.setValue('5/4/06');
18094
18095 //Pass a date string (custom format):
18096 monthField.format = 'Y-m-d';
18097 monthField.setValue('2006-5-4');
18098 </code></pre>
18099      * @param {String/Date} date The date or valid date string
18100      */
18101     setValue : function(date){
18102         Roo.log('month setValue' + date);
18103         // can only be first of month..
18104         
18105         var val = this.parseDate(date);
18106         
18107         if (this.hiddenField) {
18108             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18109         }
18110         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18111         this.value = this.parseDate(date);
18112     },
18113
18114     // private
18115     parseDate : function(value){
18116         if(!value || value instanceof Date){
18117             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18118             return value;
18119         }
18120         var v = Date.parseDate(value, this.format);
18121         if (!v && this.useIso) {
18122             v = Date.parseDate(value, 'Y-m-d');
18123         }
18124         if (v) {
18125             // 
18126             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18127         }
18128         
18129         
18130         if(!v && this.altFormats){
18131             if(!this.altFormatsArray){
18132                 this.altFormatsArray = this.altFormats.split("|");
18133             }
18134             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18135                 v = Date.parseDate(value, this.altFormatsArray[i]);
18136             }
18137         }
18138         return v;
18139     },
18140
18141     // private
18142     formatDate : function(date, fmt){
18143         return (!date || !(date instanceof Date)) ?
18144                date : date.dateFormat(fmt || this.format);
18145     },
18146
18147     // private
18148     menuListeners : {
18149         select: function(m, d){
18150             this.setValue(d);
18151             this.fireEvent('select', this, d);
18152         },
18153         show : function(){ // retain focus styling
18154             this.onFocus();
18155         },
18156         hide : function(){
18157             this.focus.defer(10, this);
18158             var ml = this.menuListeners;
18159             this.menu.un("select", ml.select,  this);
18160             this.menu.un("show", ml.show,  this);
18161             this.menu.un("hide", ml.hide,  this);
18162         }
18163     },
18164     // private
18165     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18166     onTriggerClick : function(){
18167         if(this.disabled){
18168             return;
18169         }
18170         if(this.menu == null){
18171             this.menu = new Roo.menu.DateMenu();
18172            
18173         }
18174         
18175         Roo.apply(this.menu.picker,  {
18176             
18177             showClear: this.allowBlank,
18178             minDate : this.minValue,
18179             maxDate : this.maxValue,
18180             disabledDatesRE : this.ddMatch,
18181             disabledDatesText : this.disabledDatesText,
18182             
18183             format : this.useIso ? 'Y-m-d' : this.format,
18184             minText : String.format(this.minText, this.formatDate(this.minValue)),
18185             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18186             
18187         });
18188          this.menu.on(Roo.apply({}, this.menuListeners, {
18189             scope:this
18190         }));
18191        
18192         
18193         var m = this.menu;
18194         var p = m.picker;
18195         
18196         // hide month picker get's called when we called by 'before hide';
18197         
18198         var ignorehide = true;
18199         p.hideMonthPicker  = function(disableAnim){
18200             if (ignorehide) {
18201                 return;
18202             }
18203              if(this.monthPicker){
18204                 Roo.log("hideMonthPicker called");
18205                 if(disableAnim === true){
18206                     this.monthPicker.hide();
18207                 }else{
18208                     this.monthPicker.slideOut('t', {duration:.2});
18209                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18210                     p.fireEvent("select", this, this.value);
18211                     m.hide();
18212                 }
18213             }
18214         }
18215         
18216         Roo.log('picker set value');
18217         Roo.log(this.getValue());
18218         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18219         m.show(this.el, 'tl-bl?');
18220         ignorehide  = false;
18221         // this will trigger hideMonthPicker..
18222         
18223         
18224         // hidden the day picker
18225         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18226         
18227         
18228         
18229       
18230         
18231         p.showMonthPicker.defer(100, p);
18232     
18233         
18234        
18235     },
18236
18237     beforeBlur : function(){
18238         var v = this.parseDate(this.getRawValue());
18239         if(v){
18240             this.setValue(v);
18241         }
18242     }
18243
18244     /** @cfg {Boolean} grow @hide */
18245     /** @cfg {Number} growMin @hide */
18246     /** @cfg {Number} growMax @hide */
18247     /**
18248      * @hide
18249      * @method autoSize
18250      */
18251 });/*
18252  * Based on:
18253  * Ext JS Library 1.1.1
18254  * Copyright(c) 2006-2007, Ext JS, LLC.
18255  *
18256  * Originally Released Under LGPL - original licence link has changed is not relivant.
18257  *
18258  * Fork - LGPL
18259  * <script type="text/javascript">
18260  */
18261  
18262
18263 /**
18264  * @class Roo.form.ComboBox
18265  * @extends Roo.form.TriggerField
18266  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18267  * @constructor
18268  * Create a new ComboBox.
18269  * @param {Object} config Configuration options
18270  */
18271 Roo.form.ComboBox = function(config){
18272     Roo.form.ComboBox.superclass.constructor.call(this, config);
18273     this.addEvents({
18274         /**
18275          * @event expand
18276          * Fires when the dropdown list is expanded
18277              * @param {Roo.form.ComboBox} combo This combo box
18278              */
18279         'expand' : true,
18280         /**
18281          * @event collapse
18282          * Fires when the dropdown list is collapsed
18283              * @param {Roo.form.ComboBox} combo This combo box
18284              */
18285         'collapse' : true,
18286         /**
18287          * @event beforeselect
18288          * Fires before a list item is selected. Return false to cancel the selection.
18289              * @param {Roo.form.ComboBox} combo This combo box
18290              * @param {Roo.data.Record} record The data record returned from the underlying store
18291              * @param {Number} index The index of the selected item in the dropdown list
18292              */
18293         'beforeselect' : true,
18294         /**
18295          * @event select
18296          * Fires when a list item is selected
18297              * @param {Roo.form.ComboBox} combo This combo box
18298              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18299              * @param {Number} index The index of the selected item in the dropdown list
18300              */
18301         'select' : true,
18302         /**
18303          * @event beforequery
18304          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18305          * The event object passed has these properties:
18306              * @param {Roo.form.ComboBox} combo This combo box
18307              * @param {String} query The query
18308              * @param {Boolean} forceAll true to force "all" query
18309              * @param {Boolean} cancel true to cancel the query
18310              * @param {Object} e The query event object
18311              */
18312         'beforequery': true,
18313          /**
18314          * @event add
18315          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18316              * @param {Roo.form.ComboBox} combo This combo box
18317              */
18318         'add' : true,
18319         /**
18320          * @event edit
18321          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18322              * @param {Roo.form.ComboBox} combo This combo box
18323              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18324              */
18325         'edit' : true
18326         
18327         
18328     });
18329     if(this.transform){
18330         this.allowDomMove = false;
18331         var s = Roo.getDom(this.transform);
18332         if(!this.hiddenName){
18333             this.hiddenName = s.name;
18334         }
18335         if(!this.store){
18336             this.mode = 'local';
18337             var d = [], opts = s.options;
18338             for(var i = 0, len = opts.length;i < len; i++){
18339                 var o = opts[i];
18340                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18341                 if(o.selected) {
18342                     this.value = value;
18343                 }
18344                 d.push([value, o.text]);
18345             }
18346             this.store = new Roo.data.SimpleStore({
18347                 'id': 0,
18348                 fields: ['value', 'text'],
18349                 data : d
18350             });
18351             this.valueField = 'value';
18352             this.displayField = 'text';
18353         }
18354         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18355         if(!this.lazyRender){
18356             this.target = true;
18357             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18358             s.parentNode.removeChild(s); // remove it
18359             this.render(this.el.parentNode);
18360         }else{
18361             s.parentNode.removeChild(s); // remove it
18362         }
18363
18364     }
18365     if (this.store) {
18366         this.store = Roo.factory(this.store, Roo.data);
18367     }
18368     
18369     this.selectedIndex = -1;
18370     if(this.mode == 'local'){
18371         if(config.queryDelay === undefined){
18372             this.queryDelay = 10;
18373         }
18374         if(config.minChars === undefined){
18375             this.minChars = 0;
18376         }
18377     }
18378 };
18379
18380 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18381     /**
18382      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18383      */
18384     /**
18385      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18386      * rendering into an Roo.Editor, defaults to false)
18387      */
18388     /**
18389      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18390      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18391      */
18392     /**
18393      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18394      */
18395     /**
18396      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18397      * the dropdown list (defaults to undefined, with no header element)
18398      */
18399
18400      /**
18401      * @cfg {String/Roo.Template} tpl The template to use to render the output
18402      */
18403      
18404     // private
18405     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18406     /**
18407      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18408      */
18409     listWidth: undefined,
18410     /**
18411      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18412      * mode = 'remote' or 'text' if mode = 'local')
18413      */
18414     displayField: undefined,
18415     /**
18416      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18417      * mode = 'remote' or 'value' if mode = 'local'). 
18418      * Note: use of a valueField requires the user make a selection
18419      * in order for a value to be mapped.
18420      */
18421     valueField: undefined,
18422     
18423     
18424     /**
18425      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18426      * field's data value (defaults to the underlying DOM element's name)
18427      */
18428     hiddenName: undefined,
18429     /**
18430      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18431      */
18432     listClass: '',
18433     /**
18434      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18435      */
18436     selectedClass: 'x-combo-selected',
18437     /**
18438      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18439      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18440      * which displays a downward arrow icon).
18441      */
18442     triggerClass : 'x-form-arrow-trigger',
18443     /**
18444      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18445      */
18446     shadow:'sides',
18447     /**
18448      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18449      * anchor positions (defaults to 'tl-bl')
18450      */
18451     listAlign: 'tl-bl?',
18452     /**
18453      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18454      */
18455     maxHeight: 300,
18456     /**
18457      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18458      * query specified by the allQuery config option (defaults to 'query')
18459      */
18460     triggerAction: 'query',
18461     /**
18462      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18463      * (defaults to 4, does not apply if editable = false)
18464      */
18465     minChars : 4,
18466     /**
18467      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18468      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18469      */
18470     typeAhead: false,
18471     /**
18472      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18473      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18474      */
18475     queryDelay: 500,
18476     /**
18477      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18478      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18479      */
18480     pageSize: 0,
18481     /**
18482      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18483      * when editable = true (defaults to false)
18484      */
18485     selectOnFocus:false,
18486     /**
18487      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18488      */
18489     queryParam: 'query',
18490     /**
18491      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18492      * when mode = 'remote' (defaults to 'Loading...')
18493      */
18494     loadingText: 'Loading...',
18495     /**
18496      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18497      */
18498     resizable: false,
18499     /**
18500      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18501      */
18502     handleHeight : 8,
18503     /**
18504      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18505      * traditional select (defaults to true)
18506      */
18507     editable: true,
18508     /**
18509      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18510      */
18511     allQuery: '',
18512     /**
18513      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18514      */
18515     mode: 'remote',
18516     /**
18517      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18518      * listWidth has a higher value)
18519      */
18520     minListWidth : 70,
18521     /**
18522      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18523      * allow the user to set arbitrary text into the field (defaults to false)
18524      */
18525     forceSelection:false,
18526     /**
18527      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18528      * if typeAhead = true (defaults to 250)
18529      */
18530     typeAheadDelay : 250,
18531     /**
18532      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18533      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18534      */
18535     valueNotFoundText : undefined,
18536     /**
18537      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18538      */
18539     blockFocus : false,
18540     
18541     /**
18542      * @cfg {Boolean} disableClear Disable showing of clear button.
18543      */
18544     disableClear : false,
18545     /**
18546      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18547      */
18548     alwaysQuery : false,
18549     
18550     //private
18551     addicon : false,
18552     editicon: false,
18553     
18554     // element that contains real text value.. (when hidden is used..)
18555      
18556     // private
18557     onRender : function(ct, position)
18558     {
18559         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18560         
18561         if(this.hiddenName){
18562             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18563                     'before', true);
18564             this.hiddenField.value =
18565                 this.hiddenValue !== undefined ? this.hiddenValue :
18566                 this.value !== undefined ? this.value : '';
18567
18568             // prevent input submission
18569             this.el.dom.removeAttribute('name');
18570              
18571              
18572         }
18573         
18574         if(Roo.isGecko){
18575             this.el.dom.setAttribute('autocomplete', 'off');
18576         }
18577
18578         var cls = 'x-combo-list';
18579
18580         this.list = new Roo.Layer({
18581             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18582         });
18583
18584         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18585         this.list.setWidth(lw);
18586         this.list.swallowEvent('mousewheel');
18587         this.assetHeight = 0;
18588
18589         if(this.title){
18590             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18591             this.assetHeight += this.header.getHeight();
18592         }
18593
18594         this.innerList = this.list.createChild({cls:cls+'-inner'});
18595         this.innerList.on('mouseover', this.onViewOver, this);
18596         this.innerList.on('mousemove', this.onViewMove, this);
18597         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18598         
18599         if(this.allowBlank && !this.pageSize && !this.disableClear){
18600             this.footer = this.list.createChild({cls:cls+'-ft'});
18601             this.pageTb = new Roo.Toolbar(this.footer);
18602            
18603         }
18604         if(this.pageSize){
18605             this.footer = this.list.createChild({cls:cls+'-ft'});
18606             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18607                     {pageSize: this.pageSize});
18608             
18609         }
18610         
18611         if (this.pageTb && this.allowBlank && !this.disableClear) {
18612             var _this = this;
18613             this.pageTb.add(new Roo.Toolbar.Fill(), {
18614                 cls: 'x-btn-icon x-btn-clear',
18615                 text: '&#160;',
18616                 handler: function()
18617                 {
18618                     _this.collapse();
18619                     _this.clearValue();
18620                     _this.onSelect(false, -1);
18621                 }
18622             });
18623         }
18624         if (this.footer) {
18625             this.assetHeight += this.footer.getHeight();
18626         }
18627         
18628
18629         if(!this.tpl){
18630             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18631         }
18632
18633         this.view = new Roo.View(this.innerList, this.tpl, {
18634             singleSelect:true,
18635             store: this.store,
18636             selectedClass: this.selectedClass
18637         });
18638
18639         this.view.on('click', this.onViewClick, this);
18640
18641         this.store.on('beforeload', this.onBeforeLoad, this);
18642         this.store.on('load', this.onLoad, this);
18643         this.store.on('loadexception', this.onLoadException, this);
18644
18645         if(this.resizable){
18646             this.resizer = new Roo.Resizable(this.list,  {
18647                pinned:true, handles:'se'
18648             });
18649             this.resizer.on('resize', function(r, w, h){
18650                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18651                 this.listWidth = w;
18652                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18653                 this.restrictHeight();
18654             }, this);
18655             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18656         }
18657         if(!this.editable){
18658             this.editable = true;
18659             this.setEditable(false);
18660         }  
18661         
18662         
18663         if (typeof(this.events.add.listeners) != 'undefined') {
18664             
18665             this.addicon = this.wrap.createChild(
18666                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18667        
18668             this.addicon.on('click', function(e) {
18669                 this.fireEvent('add', this);
18670             }, this);
18671         }
18672         if (typeof(this.events.edit.listeners) != 'undefined') {
18673             
18674             this.editicon = this.wrap.createChild(
18675                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18676             if (this.addicon) {
18677                 this.editicon.setStyle('margin-left', '40px');
18678             }
18679             this.editicon.on('click', function(e) {
18680                 
18681                 // we fire even  if inothing is selected..
18682                 this.fireEvent('edit', this, this.lastData );
18683                 
18684             }, this);
18685         }
18686         
18687         
18688         
18689     },
18690
18691     // private
18692     initEvents : function(){
18693         Roo.form.ComboBox.superclass.initEvents.call(this);
18694
18695         this.keyNav = new Roo.KeyNav(this.el, {
18696             "up" : function(e){
18697                 this.inKeyMode = true;
18698                 this.selectPrev();
18699             },
18700
18701             "down" : function(e){
18702                 if(!this.isExpanded()){
18703                     this.onTriggerClick();
18704                 }else{
18705                     this.inKeyMode = true;
18706                     this.selectNext();
18707                 }
18708             },
18709
18710             "enter" : function(e){
18711                 this.onViewClick();
18712                 //return true;
18713             },
18714
18715             "esc" : function(e){
18716                 this.collapse();
18717             },
18718
18719             "tab" : function(e){
18720                 this.onViewClick(false);
18721                 this.fireEvent("specialkey", this, e);
18722                 return true;
18723             },
18724
18725             scope : this,
18726
18727             doRelay : function(foo, bar, hname){
18728                 if(hname == 'down' || this.scope.isExpanded()){
18729                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18730                 }
18731                 return true;
18732             },
18733
18734             forceKeyDown: true
18735         });
18736         this.queryDelay = Math.max(this.queryDelay || 10,
18737                 this.mode == 'local' ? 10 : 250);
18738         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18739         if(this.typeAhead){
18740             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18741         }
18742         if(this.editable !== false){
18743             this.el.on("keyup", this.onKeyUp, this);
18744         }
18745         if(this.forceSelection){
18746             this.on('blur', this.doForce, this);
18747         }
18748     },
18749
18750     onDestroy : function(){
18751         if(this.view){
18752             this.view.setStore(null);
18753             this.view.el.removeAllListeners();
18754             this.view.el.remove();
18755             this.view.purgeListeners();
18756         }
18757         if(this.list){
18758             this.list.destroy();
18759         }
18760         if(this.store){
18761             this.store.un('beforeload', this.onBeforeLoad, this);
18762             this.store.un('load', this.onLoad, this);
18763             this.store.un('loadexception', this.onLoadException, this);
18764         }
18765         Roo.form.ComboBox.superclass.onDestroy.call(this);
18766     },
18767
18768     // private
18769     fireKey : function(e){
18770         if(e.isNavKeyPress() && !this.list.isVisible()){
18771             this.fireEvent("specialkey", this, e);
18772         }
18773     },
18774
18775     // private
18776     onResize: function(w, h){
18777         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18778         
18779         if(typeof w != 'number'){
18780             // we do not handle it!?!?
18781             return;
18782         }
18783         var tw = this.trigger.getWidth();
18784         tw += this.addicon ? this.addicon.getWidth() : 0;
18785         tw += this.editicon ? this.editicon.getWidth() : 0;
18786         var x = w - tw;
18787         this.el.setWidth( this.adjustWidth('input', x));
18788             
18789         this.trigger.setStyle('left', x+'px');
18790         
18791         if(this.list && this.listWidth === undefined){
18792             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18793             this.list.setWidth(lw);
18794             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18795         }
18796         
18797     
18798         
18799     },
18800
18801     /**
18802      * Allow or prevent the user from directly editing the field text.  If false is passed,
18803      * the user will only be able to select from the items defined in the dropdown list.  This method
18804      * is the runtime equivalent of setting the 'editable' config option at config time.
18805      * @param {Boolean} value True to allow the user to directly edit the field text
18806      */
18807     setEditable : function(value){
18808         if(value == this.editable){
18809             return;
18810         }
18811         this.editable = value;
18812         if(!value){
18813             this.el.dom.setAttribute('readOnly', true);
18814             this.el.on('mousedown', this.onTriggerClick,  this);
18815             this.el.addClass('x-combo-noedit');
18816         }else{
18817             this.el.dom.setAttribute('readOnly', false);
18818             this.el.un('mousedown', this.onTriggerClick,  this);
18819             this.el.removeClass('x-combo-noedit');
18820         }
18821     },
18822
18823     // private
18824     onBeforeLoad : function(){
18825         if(!this.hasFocus){
18826             return;
18827         }
18828         this.innerList.update(this.loadingText ?
18829                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18830         this.restrictHeight();
18831         this.selectedIndex = -1;
18832     },
18833
18834     // private
18835     onLoad : function(){
18836         if(!this.hasFocus){
18837             return;
18838         }
18839         if(this.store.getCount() > 0){
18840             this.expand();
18841             this.restrictHeight();
18842             if(this.lastQuery == this.allQuery){
18843                 if(this.editable){
18844                     this.el.dom.select();
18845                 }
18846                 if(!this.selectByValue(this.value, true)){
18847                     this.select(0, true);
18848                 }
18849             }else{
18850                 this.selectNext();
18851                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18852                     this.taTask.delay(this.typeAheadDelay);
18853                 }
18854             }
18855         }else{
18856             this.onEmptyResults();
18857         }
18858         //this.el.focus();
18859     },
18860     // private
18861     onLoadException : function()
18862     {
18863         this.collapse();
18864         Roo.log(this.store.reader.jsonData);
18865         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18866             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18867         }
18868         
18869         
18870     },
18871     // private
18872     onTypeAhead : function(){
18873         if(this.store.getCount() > 0){
18874             var r = this.store.getAt(0);
18875             var newValue = r.data[this.displayField];
18876             var len = newValue.length;
18877             var selStart = this.getRawValue().length;
18878             if(selStart != len){
18879                 this.setRawValue(newValue);
18880                 this.selectText(selStart, newValue.length);
18881             }
18882         }
18883     },
18884
18885     // private
18886     onSelect : function(record, index){
18887         if(this.fireEvent('beforeselect', this, record, index) !== false){
18888             this.setFromData(index > -1 ? record.data : false);
18889             this.collapse();
18890             this.fireEvent('select', this, record, index);
18891         }
18892     },
18893
18894     /**
18895      * Returns the currently selected field value or empty string if no value is set.
18896      * @return {String} value The selected value
18897      */
18898     getValue : function(){
18899         if(this.valueField){
18900             return typeof this.value != 'undefined' ? this.value : '';
18901         }
18902         return Roo.form.ComboBox.superclass.getValue.call(this);
18903     },
18904
18905     /**
18906      * Clears any text/value currently set in the field
18907      */
18908     clearValue : function(){
18909         if(this.hiddenField){
18910             this.hiddenField.value = '';
18911         }
18912         this.value = '';
18913         this.setRawValue('');
18914         this.lastSelectionText = '';
18915         
18916     },
18917
18918     /**
18919      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18920      * will be displayed in the field.  If the value does not match the data value of an existing item,
18921      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18922      * Otherwise the field will be blank (although the value will still be set).
18923      * @param {String} value The value to match
18924      */
18925     setValue : function(v){
18926         var text = v;
18927         if(this.valueField){
18928             var r = this.findRecord(this.valueField, v);
18929             if(r){
18930                 text = r.data[this.displayField];
18931             }else if(this.valueNotFoundText !== undefined){
18932                 text = this.valueNotFoundText;
18933             }
18934         }
18935         this.lastSelectionText = text;
18936         if(this.hiddenField){
18937             this.hiddenField.value = v;
18938         }
18939         Roo.form.ComboBox.superclass.setValue.call(this, text);
18940         this.value = v;
18941     },
18942     /**
18943      * @property {Object} the last set data for the element
18944      */
18945     
18946     lastData : false,
18947     /**
18948      * Sets the value of the field based on a object which is related to the record format for the store.
18949      * @param {Object} value the value to set as. or false on reset?
18950      */
18951     setFromData : function(o){
18952         var dv = ''; // display value
18953         var vv = ''; // value value..
18954         this.lastData = o;
18955         if (this.displayField) {
18956             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18957         } else {
18958             // this is an error condition!!!
18959             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18960         }
18961         
18962         if(this.valueField){
18963             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18964         }
18965         if(this.hiddenField){
18966             this.hiddenField.value = vv;
18967             
18968             this.lastSelectionText = dv;
18969             Roo.form.ComboBox.superclass.setValue.call(this, dv);
18970             this.value = vv;
18971             return;
18972         }
18973         // no hidden field.. - we store the value in 'value', but still display
18974         // display field!!!!
18975         this.lastSelectionText = dv;
18976         Roo.form.ComboBox.superclass.setValue.call(this, dv);
18977         this.value = vv;
18978         
18979         
18980     },
18981     // private
18982     reset : function(){
18983         // overridden so that last data is reset..
18984         this.setValue(this.resetValue);
18985         this.originalValue = this.getValue();
18986         this.clearInvalid();
18987         this.lastData = false;
18988         if (this.view) {
18989             this.view.clearSelections();
18990         }
18991     },
18992     // private
18993     findRecord : function(prop, value){
18994         var record;
18995         if(this.store.getCount() > 0){
18996             this.store.each(function(r){
18997                 if(r.data[prop] == value){
18998                     record = r;
18999                     return false;
19000                 }
19001                 return true;
19002             });
19003         }
19004         return record;
19005     },
19006     
19007     getName: function()
19008     {
19009         // returns hidden if it's set..
19010         if (!this.rendered) {return ''};
19011         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19012         
19013     },
19014     // private
19015     onViewMove : function(e, t){
19016         this.inKeyMode = false;
19017     },
19018
19019     // private
19020     onViewOver : function(e, t){
19021         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19022             return;
19023         }
19024         var item = this.view.findItemFromChild(t);
19025         if(item){
19026             var index = this.view.indexOf(item);
19027             this.select(index, false);
19028         }
19029     },
19030
19031     // private
19032     onViewClick : function(doFocus)
19033     {
19034         var index = this.view.getSelectedIndexes()[0];
19035         var r = this.store.getAt(index);
19036         if(r){
19037             this.onSelect(r, index);
19038         }
19039         if(doFocus !== false && !this.blockFocus){
19040             this.el.focus();
19041         }
19042     },
19043
19044     // private
19045     restrictHeight : function(){
19046         this.innerList.dom.style.height = '';
19047         var inner = this.innerList.dom;
19048         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19049         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19050         this.list.beginUpdate();
19051         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19052         this.list.alignTo(this.el, this.listAlign);
19053         this.list.endUpdate();
19054     },
19055
19056     // private
19057     onEmptyResults : function(){
19058         this.collapse();
19059     },
19060
19061     /**
19062      * Returns true if the dropdown list is expanded, else false.
19063      */
19064     isExpanded : function(){
19065         return this.list.isVisible();
19066     },
19067
19068     /**
19069      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19070      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19071      * @param {String} value The data value of the item to select
19072      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19073      * selected item if it is not currently in view (defaults to true)
19074      * @return {Boolean} True if the value matched an item in the list, else false
19075      */
19076     selectByValue : function(v, scrollIntoView){
19077         if(v !== undefined && v !== null){
19078             var r = this.findRecord(this.valueField || this.displayField, v);
19079             if(r){
19080                 this.select(this.store.indexOf(r), scrollIntoView);
19081                 return true;
19082             }
19083         }
19084         return false;
19085     },
19086
19087     /**
19088      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19089      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19090      * @param {Number} index The zero-based index of the list item to select
19091      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19092      * selected item if it is not currently in view (defaults to true)
19093      */
19094     select : function(index, scrollIntoView){
19095         this.selectedIndex = index;
19096         this.view.select(index);
19097         if(scrollIntoView !== false){
19098             var el = this.view.getNode(index);
19099             if(el){
19100                 this.innerList.scrollChildIntoView(el, false);
19101             }
19102         }
19103     },
19104
19105     // private
19106     selectNext : function(){
19107         var ct = this.store.getCount();
19108         if(ct > 0){
19109             if(this.selectedIndex == -1){
19110                 this.select(0);
19111             }else if(this.selectedIndex < ct-1){
19112                 this.select(this.selectedIndex+1);
19113             }
19114         }
19115     },
19116
19117     // private
19118     selectPrev : function(){
19119         var ct = this.store.getCount();
19120         if(ct > 0){
19121             if(this.selectedIndex == -1){
19122                 this.select(0);
19123             }else if(this.selectedIndex != 0){
19124                 this.select(this.selectedIndex-1);
19125             }
19126         }
19127     },
19128
19129     // private
19130     onKeyUp : function(e){
19131         if(this.editable !== false && !e.isSpecialKey()){
19132             this.lastKey = e.getKey();
19133             this.dqTask.delay(this.queryDelay);
19134         }
19135     },
19136
19137     // private
19138     validateBlur : function(){
19139         return !this.list || !this.list.isVisible();   
19140     },
19141
19142     // private
19143     initQuery : function(){
19144         this.doQuery(this.getRawValue());
19145     },
19146
19147     // private
19148     doForce : function(){
19149         if(this.el.dom.value.length > 0){
19150             this.el.dom.value =
19151                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19152              
19153         }
19154     },
19155
19156     /**
19157      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19158      * query allowing the query action to be canceled if needed.
19159      * @param {String} query The SQL query to execute
19160      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19161      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19162      * saved in the current store (defaults to false)
19163      */
19164     doQuery : function(q, forceAll){
19165         if(q === undefined || q === null){
19166             q = '';
19167         }
19168         var qe = {
19169             query: q,
19170             forceAll: forceAll,
19171             combo: this,
19172             cancel:false
19173         };
19174         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19175             return false;
19176         }
19177         q = qe.query;
19178         forceAll = qe.forceAll;
19179         if(forceAll === true || (q.length >= this.minChars)){
19180             if(this.lastQuery != q || this.alwaysQuery){
19181                 this.lastQuery = q;
19182                 if(this.mode == 'local'){
19183                     this.selectedIndex = -1;
19184                     if(forceAll){
19185                         this.store.clearFilter();
19186                     }else{
19187                         this.store.filter(this.displayField, q);
19188                     }
19189                     this.onLoad();
19190                 }else{
19191                     this.store.baseParams[this.queryParam] = q;
19192                     this.store.load({
19193                         params: this.getParams(q)
19194                     });
19195                     this.expand();
19196                 }
19197             }else{
19198                 this.selectedIndex = -1;
19199                 this.onLoad();   
19200             }
19201         }
19202     },
19203
19204     // private
19205     getParams : function(q){
19206         var p = {};
19207         //p[this.queryParam] = q;
19208         if(this.pageSize){
19209             p.start = 0;
19210             p.limit = this.pageSize;
19211         }
19212         return p;
19213     },
19214
19215     /**
19216      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19217      */
19218     collapse : function(){
19219         if(!this.isExpanded()){
19220             return;
19221         }
19222         this.list.hide();
19223         Roo.get(document).un('mousedown', this.collapseIf, this);
19224         Roo.get(document).un('mousewheel', this.collapseIf, this);
19225         if (!this.editable) {
19226             Roo.get(document).un('keydown', this.listKeyPress, this);
19227         }
19228         this.fireEvent('collapse', this);
19229     },
19230
19231     // private
19232     collapseIf : function(e){
19233         if(!e.within(this.wrap) && !e.within(this.list)){
19234             this.collapse();
19235         }
19236     },
19237
19238     /**
19239      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19240      */
19241     expand : function(){
19242         if(this.isExpanded() || !this.hasFocus){
19243             return;
19244         }
19245         this.list.alignTo(this.el, this.listAlign);
19246         this.list.show();
19247         Roo.get(document).on('mousedown', this.collapseIf, this);
19248         Roo.get(document).on('mousewheel', this.collapseIf, this);
19249         if (!this.editable) {
19250             Roo.get(document).on('keydown', this.listKeyPress, this);
19251         }
19252         
19253         this.fireEvent('expand', this);
19254     },
19255
19256     // private
19257     // Implements the default empty TriggerField.onTriggerClick function
19258     onTriggerClick : function(){
19259         if(this.disabled){
19260             return;
19261         }
19262         if(this.isExpanded()){
19263             this.collapse();
19264             if (!this.blockFocus) {
19265                 this.el.focus();
19266             }
19267             
19268         }else {
19269             this.hasFocus = true;
19270             if(this.triggerAction == 'all') {
19271                 this.doQuery(this.allQuery, true);
19272             } else {
19273                 this.doQuery(this.getRawValue());
19274             }
19275             if (!this.blockFocus) {
19276                 this.el.focus();
19277             }
19278         }
19279     },
19280     listKeyPress : function(e)
19281     {
19282         //Roo.log('listkeypress');
19283         // scroll to first matching element based on key pres..
19284         if (e.isSpecialKey()) {
19285             return false;
19286         }
19287         var k = String.fromCharCode(e.getKey()).toUpperCase();
19288         //Roo.log(k);
19289         var match  = false;
19290         var csel = this.view.getSelectedNodes();
19291         var cselitem = false;
19292         if (csel.length) {
19293             var ix = this.view.indexOf(csel[0]);
19294             cselitem  = this.store.getAt(ix);
19295             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19296                 cselitem = false;
19297             }
19298             
19299         }
19300         
19301         this.store.each(function(v) { 
19302             if (cselitem) {
19303                 // start at existing selection.
19304                 if (cselitem.id == v.id) {
19305                     cselitem = false;
19306                 }
19307                 return;
19308             }
19309                 
19310             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19311                 match = this.store.indexOf(v);
19312                 return false;
19313             }
19314         }, this);
19315         
19316         if (match === false) {
19317             return true; // no more action?
19318         }
19319         // scroll to?
19320         this.view.select(match);
19321         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19322         sn.scrollIntoView(sn.dom.parentNode, false);
19323     } 
19324
19325     /** 
19326     * @cfg {Boolean} grow 
19327     * @hide 
19328     */
19329     /** 
19330     * @cfg {Number} growMin 
19331     * @hide 
19332     */
19333     /** 
19334     * @cfg {Number} growMax 
19335     * @hide 
19336     */
19337     /**
19338      * @hide
19339      * @method autoSize
19340      */
19341 });/*
19342  * Copyright(c) 2010-2012, Roo J Solutions Limited
19343  *
19344  * Licence LGPL
19345  *
19346  */
19347
19348 /**
19349  * @class Roo.form.ComboBoxArray
19350  * @extends Roo.form.TextField
19351  * A facebook style adder... for lists of email / people / countries  etc...
19352  * pick multiple items from a combo box, and shows each one.
19353  *
19354  *  Fred [x]  Brian [x]  [Pick another |v]
19355  *
19356  *
19357  *  For this to work: it needs various extra information
19358  *    - normal combo problay has
19359  *      name, hiddenName
19360  *    + displayField, valueField
19361  *
19362  *    For our purpose...
19363  *
19364  *
19365  *   If we change from 'extends' to wrapping...
19366  *   
19367  *  
19368  *
19369  
19370  
19371  * @constructor
19372  * Create a new ComboBoxArray.
19373  * @param {Object} config Configuration options
19374  */
19375  
19376
19377 Roo.form.ComboBoxArray = function(config)
19378 {
19379     this.addEvents({
19380         /**
19381          * @event beforeremove
19382          * Fires before remove the value from the list
19383              * @param {Roo.form.ComboBoxArray} _self This combo box array
19384              * @param {Roo.form.ComboBoxArray.Item} item removed item
19385              */
19386         'beforeremove' : true,
19387         /**
19388          * @event remove
19389          * Fires when remove the value from the list
19390              * @param {Roo.form.ComboBoxArray} _self This combo box array
19391              * @param {Roo.form.ComboBoxArray.Item} item removed item
19392              */
19393         'remove' : true
19394         
19395         
19396     });
19397     
19398     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19399     
19400     this.items = new Roo.util.MixedCollection(false);
19401     
19402     // construct the child combo...
19403     
19404     
19405     
19406     
19407    
19408     
19409 }
19410
19411  
19412 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19413
19414     /**
19415      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19416      */
19417     
19418     lastData : false,
19419     
19420     // behavies liek a hiddne field
19421     inputType:      'hidden',
19422     /**
19423      * @cfg {Number} width The width of the box that displays the selected element
19424      */ 
19425     width:          300,
19426
19427     
19428     
19429     /**
19430      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19431      */
19432     name : false,
19433     /**
19434      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19435      */
19436     hiddenName : false,
19437       /**
19438      * @cfg {String} seperator    The value seperator normally ',' 
19439      */
19440     seperator : ',',
19441     
19442     // private the array of items that are displayed..
19443     items  : false,
19444     // private - the hidden field el.
19445     hiddenEl : false,
19446     // private - the filed el..
19447     el : false,
19448     
19449     //validateValue : function() { return true; }, // all values are ok!
19450     //onAddClick: function() { },
19451     
19452     onRender : function(ct, position) 
19453     {
19454         
19455         // create the standard hidden element
19456         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19457         
19458         
19459         // give fake names to child combo;
19460         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19461         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19462         
19463         this.combo = Roo.factory(this.combo, Roo.form);
19464         this.combo.onRender(ct, position);
19465         if (typeof(this.combo.width) != 'undefined') {
19466             this.combo.onResize(this.combo.width,0);
19467         }
19468         
19469         this.combo.initEvents();
19470         
19471         // assigned so form know we need to do this..
19472         this.store          = this.combo.store;
19473         this.valueField     = this.combo.valueField;
19474         this.displayField   = this.combo.displayField ;
19475         
19476         
19477         this.combo.wrap.addClass('x-cbarray-grp');
19478         
19479         var cbwrap = this.combo.wrap.createChild(
19480             {tag: 'div', cls: 'x-cbarray-cb'},
19481             this.combo.el.dom
19482         );
19483         
19484              
19485         this.hiddenEl = this.combo.wrap.createChild({
19486             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19487         });
19488         this.el = this.combo.wrap.createChild({
19489             tag: 'input',  type:'hidden' , name: this.name, value : ''
19490         });
19491          //   this.el.dom.removeAttribute("name");
19492         
19493         
19494         this.outerWrap = this.combo.wrap;
19495         this.wrap = cbwrap;
19496         
19497         this.outerWrap.setWidth(this.width);
19498         this.outerWrap.dom.removeChild(this.el.dom);
19499         
19500         this.wrap.dom.appendChild(this.el.dom);
19501         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19502         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19503         
19504         this.combo.trigger.setStyle('position','relative');
19505         this.combo.trigger.setStyle('left', '0px');
19506         this.combo.trigger.setStyle('top', '2px');
19507         
19508         this.combo.el.setStyle('vertical-align', 'text-bottom');
19509         
19510         //this.trigger.setStyle('vertical-align', 'top');
19511         
19512         // this should use the code from combo really... on('add' ....)
19513         if (this.adder) {
19514             
19515         
19516             this.adder = this.outerWrap.createChild(
19517                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19518             var _t = this;
19519             this.adder.on('click', function(e) {
19520                 _t.fireEvent('adderclick', this, e);
19521             }, _t);
19522         }
19523         //var _t = this;
19524         //this.adder.on('click', this.onAddClick, _t);
19525         
19526         
19527         this.combo.on('select', function(cb, rec, ix) {
19528             this.addItem(rec.data);
19529             
19530             cb.setValue('');
19531             cb.el.dom.value = '';
19532             //cb.lastData = rec.data;
19533             // add to list
19534             
19535         }, this);
19536         
19537         
19538     },
19539     
19540     
19541     getName: function()
19542     {
19543         // returns hidden if it's set..
19544         if (!this.rendered) {return ''};
19545         return  this.hiddenName ? this.hiddenName : this.name;
19546         
19547     },
19548     
19549     
19550     onResize: function(w, h){
19551         
19552         return;
19553         // not sure if this is needed..
19554         //this.combo.onResize(w,h);
19555         
19556         if(typeof w != 'number'){
19557             // we do not handle it!?!?
19558             return;
19559         }
19560         var tw = this.combo.trigger.getWidth();
19561         tw += this.addicon ? this.addicon.getWidth() : 0;
19562         tw += this.editicon ? this.editicon.getWidth() : 0;
19563         var x = w - tw;
19564         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19565             
19566         this.combo.trigger.setStyle('left', '0px');
19567         
19568         if(this.list && this.listWidth === undefined){
19569             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19570             this.list.setWidth(lw);
19571             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19572         }
19573         
19574     
19575         
19576     },
19577     
19578     addItem: function(rec)
19579     {
19580         var valueField = this.combo.valueField;
19581         var displayField = this.combo.displayField;
19582         
19583         if (this.items.indexOfKey(rec[valueField]) > -1) {
19584             //console.log("GOT " + rec.data.id);
19585             return;
19586         }
19587         
19588         var x = new Roo.form.ComboBoxArray.Item({
19589             //id : rec[this.idField],
19590             data : rec,
19591             displayField : displayField ,
19592             tipField : displayField ,
19593             cb : this
19594         });
19595         // use the 
19596         this.items.add(rec[valueField],x);
19597         // add it before the element..
19598         this.updateHiddenEl();
19599         x.render(this.outerWrap, this.wrap.dom);
19600         // add the image handler..
19601     },
19602     
19603     updateHiddenEl : function()
19604     {
19605         this.validate();
19606         if (!this.hiddenEl) {
19607             return;
19608         }
19609         var ar = [];
19610         var idField = this.combo.valueField;
19611         
19612         this.items.each(function(f) {
19613             ar.push(f.data[idField]);
19614         });
19615         this.hiddenEl.dom.value = ar.join(this.seperator);
19616         this.validate();
19617     },
19618     
19619     reset : function()
19620     {
19621         this.items.clear();
19622         
19623         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19624            el.remove();
19625         });
19626         
19627         this.el.dom.value = '';
19628         if (this.hiddenEl) {
19629             this.hiddenEl.dom.value = '';
19630         }
19631         
19632     },
19633     getValue: function()
19634     {
19635         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19636     },
19637     setValue: function(v) // not a valid action - must use addItems..
19638     {
19639         
19640         this.reset();
19641          
19642         if (this.store.isLocal && (typeof(v) == 'string')) {
19643             // then we can use the store to find the values..
19644             // comma seperated at present.. this needs to allow JSON based encoding..
19645             this.hiddenEl.value  = v;
19646             var v_ar = [];
19647             Roo.each(v.split(this.seperator), function(k) {
19648                 Roo.log("CHECK " + this.valueField + ',' + k);
19649                 var li = this.store.query(this.valueField, k);
19650                 if (!li.length) {
19651                     return;
19652                 }
19653                 var add = {};
19654                 add[this.valueField] = k;
19655                 add[this.displayField] = li.item(0).data[this.displayField];
19656                 
19657                 this.addItem(add);
19658             }, this) 
19659              
19660         }
19661         if (typeof(v) == 'object' ) {
19662             // then let's assume it's an array of objects..
19663             Roo.each(v, function(l) {
19664                 var add = l;
19665                 if (typeof(l) == 'string') {
19666                     add = {};
19667                     add[this.valueField] = l;
19668                     add[this.displayField] = l
19669                 }
19670                 this.addItem(add);
19671             }, this);
19672              
19673         }
19674         
19675         
19676     },
19677     setFromData: function(v)
19678     {
19679         // this recieves an object, if setValues is called.
19680         this.reset();
19681         this.el.dom.value = v[this.displayField];
19682         this.hiddenEl.dom.value = v[this.valueField];
19683         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19684             return;
19685         }
19686         var kv = v[this.valueField];
19687         var dv = v[this.displayField];
19688         kv = typeof(kv) != 'string' ? '' : kv;
19689         dv = typeof(dv) != 'string' ? '' : dv;
19690         
19691         
19692         var keys = kv.split(this.seperator);
19693         var display = dv.split(this.seperator);
19694         for (var i = 0 ; i < keys.length; i++) {
19695             add = {};
19696             add[this.valueField] = keys[i];
19697             add[this.displayField] = display[i];
19698             this.addItem(add);
19699         }
19700       
19701         
19702     },
19703     
19704     /**
19705      * Validates the combox array value
19706      * @return {Boolean} True if the value is valid, else false
19707      */
19708     validate : function(){
19709         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19710             this.clearInvalid();
19711             return true;
19712         }
19713         return false;
19714     },
19715     
19716     validateValue : function(value){
19717         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19718         
19719     },
19720     
19721     /*@
19722      * overide
19723      * 
19724      */
19725     isDirty : function() {
19726         if(this.disabled) {
19727             return false;
19728         }
19729         
19730         try {
19731             var d = Roo.decode(String(this.originalValue));
19732         } catch (e) {
19733             return String(this.getValue()) !== String(this.originalValue);
19734         }
19735         
19736         var originalValue = [];
19737         
19738         for (var i = 0; i < d.length; i++){
19739             originalValue.push(d[i][this.valueField]);
19740         }
19741         
19742         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19743         
19744     }
19745     
19746 });
19747
19748
19749
19750 /**
19751  * @class Roo.form.ComboBoxArray.Item
19752  * @extends Roo.BoxComponent
19753  * A selected item in the list
19754  *  Fred [x]  Brian [x]  [Pick another |v]
19755  * 
19756  * @constructor
19757  * Create a new item.
19758  * @param {Object} config Configuration options
19759  */
19760  
19761 Roo.form.ComboBoxArray.Item = function(config) {
19762     config.id = Roo.id();
19763     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19764 }
19765
19766 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19767     data : {},
19768     cb: false,
19769     displayField : false,
19770     tipField : false,
19771     
19772     
19773     defaultAutoCreate : {
19774         tag: 'div',
19775         cls: 'x-cbarray-item',
19776         cn : [ 
19777             { tag: 'div' },
19778             {
19779                 tag: 'img',
19780                 width:16,
19781                 height : 16,
19782                 src : Roo.BLANK_IMAGE_URL ,
19783                 align: 'center'
19784             }
19785         ]
19786         
19787     },
19788     
19789  
19790     onRender : function(ct, position)
19791     {
19792         Roo.form.Field.superclass.onRender.call(this, ct, position);
19793         
19794         if(!this.el){
19795             var cfg = this.getAutoCreate();
19796             this.el = ct.createChild(cfg, position);
19797         }
19798         
19799         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19800         
19801         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19802             this.cb.renderer(this.data) :
19803             String.format('{0}',this.data[this.displayField]);
19804         
19805             
19806         this.el.child('div').dom.setAttribute('qtip',
19807                         String.format('{0}',this.data[this.tipField])
19808         );
19809         
19810         this.el.child('img').on('click', this.remove, this);
19811         
19812     },
19813    
19814     remove : function()
19815     {
19816         if(this.cb.disabled){
19817             return;
19818         }
19819         
19820         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19821             this.cb.items.remove(this);
19822             this.el.child('img').un('click', this.remove, this);
19823             this.el.remove();
19824             this.cb.updateHiddenEl();
19825
19826             this.cb.fireEvent('remove', this.cb, this);
19827         }
19828         
19829     }
19830 });/*
19831  * RooJS Library 1.1.1
19832  * Copyright(c) 2008-2011  Alan Knowles
19833  *
19834  * License - LGPL
19835  */
19836  
19837
19838 /**
19839  * @class Roo.form.ComboNested
19840  * @extends Roo.form.ComboBox
19841  * A combobox for that allows selection of nested items in a list,
19842  * eg.
19843  *
19844  *  Book
19845  *    -> red
19846  *    -> green
19847  *  Table
19848  *    -> square
19849  *      ->red
19850  *      ->green
19851  *    -> rectangle
19852  *      ->green
19853  *      
19854  * 
19855  * @constructor
19856  * Create a new ComboNested
19857  * @param {Object} config Configuration options
19858  */
19859 Roo.form.ComboNested = function(config){
19860     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19861     // should verify some data...
19862     // like
19863     // hiddenName = required..
19864     // displayField = required
19865     // valudField == required
19866     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19867     var _t = this;
19868     Roo.each(req, function(e) {
19869         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19870             throw "Roo.form.ComboNested : missing value for: " + e;
19871         }
19872     });
19873      
19874     
19875 };
19876
19877 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19878    
19879     /*
19880      * @config {Number} max Number of columns to show
19881      */
19882     
19883     maxColumns : 3,
19884    
19885     list : null, // the outermost div..
19886     innerLists : null, // the
19887     views : null,
19888     stores : null,
19889     // private
19890     loadingChildren : false,
19891     
19892     onRender : function(ct, position)
19893     {
19894         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19895         
19896         if(this.hiddenName){
19897             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19898                     'before', true);
19899             this.hiddenField.value =
19900                 this.hiddenValue !== undefined ? this.hiddenValue :
19901                 this.value !== undefined ? this.value : '';
19902
19903             // prevent input submission
19904             this.el.dom.removeAttribute('name');
19905              
19906              
19907         }
19908         
19909         if(Roo.isGecko){
19910             this.el.dom.setAttribute('autocomplete', 'off');
19911         }
19912
19913         var cls = 'x-combo-list';
19914
19915         this.list = new Roo.Layer({
19916             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19917         });
19918
19919         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19920         this.list.setWidth(lw);
19921         this.list.swallowEvent('mousewheel');
19922         this.assetHeight = 0;
19923
19924         if(this.title){
19925             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19926             this.assetHeight += this.header.getHeight();
19927         }
19928         this.innerLists = [];
19929         this.views = [];
19930         this.stores = [];
19931         for (var i =0 ; i < this.maxColumns; i++) {
19932             this.onRenderList( cls, i);
19933         }
19934         
19935         // always needs footer, as we are going to have an 'OK' button.
19936         this.footer = this.list.createChild({cls:cls+'-ft'});
19937         this.pageTb = new Roo.Toolbar(this.footer);  
19938         var _this = this;
19939         this.pageTb.add(  {
19940             
19941             text: 'Done',
19942             handler: function()
19943             {
19944                 _this.collapse();
19945             }
19946         });
19947         
19948         if ( this.allowBlank && !this.disableClear) {
19949             
19950             this.pageTb.add(new Roo.Toolbar.Fill(), {
19951                 cls: 'x-btn-icon x-btn-clear',
19952                 text: '&#160;',
19953                 handler: function()
19954                 {
19955                     _this.collapse();
19956                     _this.clearValue();
19957                     _this.onSelect(false, -1);
19958                 }
19959             });
19960         }
19961         if (this.footer) {
19962             this.assetHeight += this.footer.getHeight();
19963         }
19964         
19965     },
19966     onRenderList : function (  cls, i)
19967     {
19968         
19969         var lw = Math.floor(
19970                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
19971         );
19972         
19973         this.list.setWidth(lw); // default to '1'
19974
19975         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
19976         //il.on('mouseover', this.onViewOver, this, { list:  i });
19977         //il.on('mousemove', this.onViewMove, this, { list:  i });
19978         il.setWidth(lw);
19979         il.setStyle({ 'overflow-x' : 'hidden'});
19980
19981         if(!this.tpl){
19982             this.tpl = new Roo.Template({
19983                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
19984                 isEmpty: function (value, allValues) {
19985                     //Roo.log(value);
19986                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
19987                     return dl ? 'has-children' : 'no-children'
19988                 }
19989             });
19990         }
19991         
19992         var store  = this.store;
19993         if (i > 0) {
19994             store  = new Roo.data.SimpleStore({
19995                 //fields : this.store.reader.meta.fields,
19996                 reader : this.store.reader,
19997                 data : [ ]
19998             });
19999         }
20000         this.stores[i]  = store;
20001                   
20002         var view = this.views[i] = new Roo.View(
20003             il,
20004             this.tpl,
20005             {
20006                 singleSelect:true,
20007                 store: store,
20008                 selectedClass: this.selectedClass
20009             }
20010         );
20011         view.getEl().setWidth(lw);
20012         view.getEl().setStyle({
20013             position: i < 1 ? 'relative' : 'absolute',
20014             top: 0,
20015             left: (i * lw ) + 'px',
20016             display : i > 0 ? 'none' : 'block'
20017         });
20018         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20019         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20020         //view.on('click', this.onViewClick, this, { list : i });
20021
20022         store.on('beforeload', this.onBeforeLoad, this);
20023         store.on('load',  this.onLoad, this, { list  : i});
20024         store.on('loadexception', this.onLoadException, this);
20025
20026         // hide the other vies..
20027         
20028         
20029         
20030     },
20031       
20032     restrictHeight : function()
20033     {
20034         var mh = 0;
20035         Roo.each(this.innerLists, function(il,i) {
20036             var el = this.views[i].getEl();
20037             el.dom.style.height = '';
20038             var inner = el.dom;
20039             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20040             // only adjust heights on other ones..
20041             mh = Math.max(h, mh);
20042             if (i < 1) {
20043                 
20044                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20045                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20046                
20047             }
20048             
20049             
20050         }, this);
20051         
20052         this.list.beginUpdate();
20053         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20054         this.list.alignTo(this.el, this.listAlign);
20055         this.list.endUpdate();
20056         
20057     },
20058      
20059     
20060     // -- store handlers..
20061     // private
20062     onBeforeLoad : function()
20063     {
20064         if(!this.hasFocus){
20065             return;
20066         }
20067         this.innerLists[0].update(this.loadingText ?
20068                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20069         this.restrictHeight();
20070         this.selectedIndex = -1;
20071     },
20072     // private
20073     onLoad : function(a,b,c,d)
20074     {
20075         if (!this.loadingChildren) {
20076             // then we are loading the top level. - hide the children
20077             for (var i = 1;i < this.views.length; i++) {
20078                 this.views[i].getEl().setStyle({ display : 'none' });
20079             }
20080             var lw = Math.floor(
20081                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20082             );
20083         
20084              this.list.setWidth(lw); // default to '1'
20085
20086             
20087         }
20088         if(!this.hasFocus){
20089             return;
20090         }
20091         
20092         if(this.store.getCount() > 0) {
20093             this.expand();
20094             this.restrictHeight();   
20095         } else {
20096             this.onEmptyResults();
20097         }
20098         
20099         if (!this.loadingChildren) {
20100             this.selectActive();
20101         }
20102         /*
20103         this.stores[1].loadData([]);
20104         this.stores[2].loadData([]);
20105         this.views
20106         */    
20107     
20108         //this.el.focus();
20109     },
20110     
20111     
20112     // private
20113     onLoadException : function()
20114     {
20115         this.collapse();
20116         Roo.log(this.store.reader.jsonData);
20117         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20118             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20119         }
20120         
20121         
20122     },
20123     // no cleaning of leading spaces on blur here.
20124     cleanLeadingSpace : function(e) { },
20125     
20126
20127     onSelectChange : function (view, sels, opts )
20128     {
20129         var ix = view.getSelectedIndexes();
20130          
20131         if (opts.list > this.maxColumns - 2) {
20132             if (view.store.getCount()<  1) {
20133                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20134
20135             } else  {
20136                 if (ix.length) {
20137                     // used to clear ?? but if we are loading unselected 
20138                     this.setFromData(view.store.getAt(ix[0]).data);
20139                 }
20140                 
20141             }
20142             
20143             return;
20144         }
20145         
20146         if (!ix.length) {
20147             // this get's fired when trigger opens..
20148            // this.setFromData({});
20149             var str = this.stores[opts.list+1];
20150             str.data.clear(); // removeall wihtout the fire events..
20151             return;
20152         }
20153         
20154         var rec = view.store.getAt(ix[0]);
20155          
20156         this.setFromData(rec.data);
20157         this.fireEvent('select', this, rec, ix[0]);
20158         
20159         var lw = Math.floor(
20160              (
20161                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20162              ) / this.maxColumns
20163         );
20164         this.loadingChildren = true;
20165         this.stores[opts.list+1].loadDataFromChildren( rec );
20166         this.loadingChildren = false;
20167         var dl = this.stores[opts.list+1]. getTotalCount();
20168         
20169         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20170         
20171         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20172         for (var i = opts.list+2; i < this.views.length;i++) {
20173             this.views[i].getEl().setStyle({ display : 'none' });
20174         }
20175         
20176         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20177         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20178         
20179         if (this.isLoading) {
20180            // this.selectActive(opts.list);
20181         }
20182          
20183     },
20184     
20185     
20186     
20187     
20188     onDoubleClick : function()
20189     {
20190         this.collapse(); //??
20191     },
20192     
20193      
20194     
20195     
20196     
20197     // private
20198     recordToStack : function(store, prop, value, stack)
20199     {
20200         var cstore = new Roo.data.SimpleStore({
20201             //fields : this.store.reader.meta.fields, // we need array reader.. for
20202             reader : this.store.reader,
20203             data : [ ]
20204         });
20205         var _this = this;
20206         var record  = false;
20207         var srec = false;
20208         if(store.getCount() < 1){
20209             return false;
20210         }
20211         store.each(function(r){
20212             if(r.data[prop] == value){
20213                 record = r;
20214             srec = r;
20215                 return false;
20216             }
20217             if (r.data.cn && r.data.cn.length) {
20218                 cstore.loadDataFromChildren( r);
20219                 var cret = _this.recordToStack(cstore, prop, value, stack);
20220                 if (cret !== false) {
20221                     record = cret;
20222                     srec = r;
20223                     return false;
20224                 }
20225             }
20226              
20227             return true;
20228         });
20229         if (record == false) {
20230             return false
20231         }
20232         stack.unshift(srec);
20233         return record;
20234     },
20235     
20236     /*
20237      * find the stack of stores that match our value.
20238      *
20239      * 
20240      */
20241     
20242     selectActive : function ()
20243     {
20244         // if store is not loaded, then we will need to wait for that to happen first.
20245         var stack = [];
20246         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20247         for (var i = 0; i < stack.length; i++ ) {
20248             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20249         }
20250         
20251     }
20252         
20253          
20254     
20255     
20256     
20257     
20258 });/*
20259  * Based on:
20260  * Ext JS Library 1.1.1
20261  * Copyright(c) 2006-2007, Ext JS, LLC.
20262  *
20263  * Originally Released Under LGPL - original licence link has changed is not relivant.
20264  *
20265  * Fork - LGPL
20266  * <script type="text/javascript">
20267  */
20268 /**
20269  * @class Roo.form.Checkbox
20270  * @extends Roo.form.Field
20271  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20272  * @constructor
20273  * Creates a new Checkbox
20274  * @param {Object} config Configuration options
20275  */
20276 Roo.form.Checkbox = function(config){
20277     Roo.form.Checkbox.superclass.constructor.call(this, config);
20278     this.addEvents({
20279         /**
20280          * @event check
20281          * Fires when the checkbox is checked or unchecked.
20282              * @param {Roo.form.Checkbox} this This checkbox
20283              * @param {Boolean} checked The new checked value
20284              */
20285         check : true
20286     });
20287 };
20288
20289 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20290     /**
20291      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20292      */
20293     focusClass : undefined,
20294     /**
20295      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20296      */
20297     fieldClass: "x-form-field",
20298     /**
20299      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20300      */
20301     checked: false,
20302     /**
20303      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20304      * {tag: "input", type: "checkbox", autocomplete: "off"})
20305      */
20306     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20307     /**
20308      * @cfg {String} boxLabel The text that appears beside the checkbox
20309      */
20310     boxLabel : "",
20311     /**
20312      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20313      */  
20314     inputValue : '1',
20315     /**
20316      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20317      */
20318      valueOff: '0', // value when not checked..
20319
20320     actionMode : 'viewEl', 
20321     //
20322     // private
20323     itemCls : 'x-menu-check-item x-form-item',
20324     groupClass : 'x-menu-group-item',
20325     inputType : 'hidden',
20326     
20327     
20328     inSetChecked: false, // check that we are not calling self...
20329     
20330     inputElement: false, // real input element?
20331     basedOn: false, // ????
20332     
20333     isFormField: true, // not sure where this is needed!!!!
20334
20335     onResize : function(){
20336         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20337         if(!this.boxLabel){
20338             this.el.alignTo(this.wrap, 'c-c');
20339         }
20340     },
20341
20342     initEvents : function(){
20343         Roo.form.Checkbox.superclass.initEvents.call(this);
20344         this.el.on("click", this.onClick,  this);
20345         this.el.on("change", this.onClick,  this);
20346     },
20347
20348
20349     getResizeEl : function(){
20350         return this.wrap;
20351     },
20352
20353     getPositionEl : function(){
20354         return this.wrap;
20355     },
20356
20357     // private
20358     onRender : function(ct, position){
20359         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20360         /*
20361         if(this.inputValue !== undefined){
20362             this.el.dom.value = this.inputValue;
20363         }
20364         */
20365         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20366         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20367         var viewEl = this.wrap.createChild({ 
20368             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20369         this.viewEl = viewEl;   
20370         this.wrap.on('click', this.onClick,  this); 
20371         
20372         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20373         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20374         
20375         
20376         
20377         if(this.boxLabel){
20378             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20379         //    viewEl.on('click', this.onClick,  this); 
20380         }
20381         //if(this.checked){
20382             this.setChecked(this.checked);
20383         //}else{
20384             //this.checked = this.el.dom;
20385         //}
20386
20387     },
20388
20389     // private
20390     initValue : Roo.emptyFn,
20391
20392     /**
20393      * Returns the checked state of the checkbox.
20394      * @return {Boolean} True if checked, else false
20395      */
20396     getValue : function(){
20397         if(this.el){
20398             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20399         }
20400         return this.valueOff;
20401         
20402     },
20403
20404         // private
20405     onClick : function(){ 
20406         if (this.disabled) {
20407             return;
20408         }
20409         this.setChecked(!this.checked);
20410
20411         //if(this.el.dom.checked != this.checked){
20412         //    this.setValue(this.el.dom.checked);
20413        // }
20414     },
20415
20416     /**
20417      * Sets the checked state of the checkbox.
20418      * On is always based on a string comparison between inputValue and the param.
20419      * @param {Boolean/String} value - the value to set 
20420      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20421      */
20422     setValue : function(v,suppressEvent){
20423         
20424         
20425         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20426         //if(this.el && this.el.dom){
20427         //    this.el.dom.checked = this.checked;
20428         //    this.el.dom.defaultChecked = this.checked;
20429         //}
20430         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20431         //this.fireEvent("check", this, this.checked);
20432     },
20433     // private..
20434     setChecked : function(state,suppressEvent)
20435     {
20436         if (this.inSetChecked) {
20437             this.checked = state;
20438             return;
20439         }
20440         
20441     
20442         if(this.wrap){
20443             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20444         }
20445         this.checked = state;
20446         if(suppressEvent !== true){
20447             this.fireEvent('check', this, state);
20448         }
20449         this.inSetChecked = true;
20450         this.el.dom.value = state ? this.inputValue : this.valueOff;
20451         this.inSetChecked = false;
20452         
20453     },
20454     // handle setting of hidden value by some other method!!?!?
20455     setFromHidden: function()
20456     {
20457         if(!this.el){
20458             return;
20459         }
20460         //console.log("SET FROM HIDDEN");
20461         //alert('setFrom hidden');
20462         this.setValue(this.el.dom.value);
20463     },
20464     
20465     onDestroy : function()
20466     {
20467         if(this.viewEl){
20468             Roo.get(this.viewEl).remove();
20469         }
20470          
20471         Roo.form.Checkbox.superclass.onDestroy.call(this);
20472     },
20473     
20474     setBoxLabel : function(str)
20475     {
20476         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20477     }
20478
20479 });/*
20480  * Based on:
20481  * Ext JS Library 1.1.1
20482  * Copyright(c) 2006-2007, Ext JS, LLC.
20483  *
20484  * Originally Released Under LGPL - original licence link has changed is not relivant.
20485  *
20486  * Fork - LGPL
20487  * <script type="text/javascript">
20488  */
20489  
20490 /**
20491  * @class Roo.form.Radio
20492  * @extends Roo.form.Checkbox
20493  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20494  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20495  * @constructor
20496  * Creates a new Radio
20497  * @param {Object} config Configuration options
20498  */
20499 Roo.form.Radio = function(){
20500     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20501 };
20502 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20503     inputType: 'radio',
20504
20505     /**
20506      * If this radio is part of a group, it will return the selected value
20507      * @return {String}
20508      */
20509     getGroupValue : function(){
20510         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20511     },
20512     
20513     
20514     onRender : function(ct, position){
20515         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20516         
20517         if(this.inputValue !== undefined){
20518             this.el.dom.value = this.inputValue;
20519         }
20520          
20521         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20522         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20523         //var viewEl = this.wrap.createChild({ 
20524         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20525         //this.viewEl = viewEl;   
20526         //this.wrap.on('click', this.onClick,  this); 
20527         
20528         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20529         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20530         
20531         
20532         
20533         if(this.boxLabel){
20534             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20535         //    viewEl.on('click', this.onClick,  this); 
20536         }
20537          if(this.checked){
20538             this.el.dom.checked =   'checked' ;
20539         }
20540          
20541     } 
20542     
20543     
20544 });Roo.rtf = {}; // namespace
20545 Roo.rtf.Hex = function(hex)
20546 {
20547     this.hexstr = hex;
20548 };
20549 Roo.rtf.Paragraph = function(opts)
20550 {
20551     this.content = []; ///??? is that used?
20552 };Roo.rtf.Span = function(opts)
20553 {
20554     this.value = opts.value;
20555 };
20556
20557 Roo.rtf.Group = function(parent)
20558 {
20559     // we dont want to acutally store parent - it will make debug a nightmare..
20560     this.content = [];
20561     this.cn  = [];
20562      
20563        
20564     
20565 };
20566
20567 Roo.rtf.Group.prototype = {
20568     ignorable : false,
20569     content: false,
20570     cn: false,
20571     addContent : function(node) {
20572         // could set styles...
20573         this.content.push(node);
20574     },
20575     addChild : function(cn)
20576     {
20577         this.cn.push(cn);
20578     },
20579     // only for images really...
20580     toDataURL : function()
20581     {
20582         var mimetype = false;
20583         switch(true) {
20584             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
20585                 mimetype = "image/png";
20586                 break;
20587              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
20588                 mimetype = "image/jpeg";
20589                 break;
20590             default :
20591                 return 'about:blank'; // ?? error?
20592         }
20593         
20594         
20595         var hexstring = this.content[this.content.length-1].value;
20596         
20597         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
20598             return String.fromCharCode(parseInt(a, 16));
20599         }).join(""));
20600     }
20601     
20602 };
20603 // this looks like it's normally the {rtf{ .... }}
20604 Roo.rtf.Document = function()
20605 {
20606     // we dont want to acutally store parent - it will make debug a nightmare..
20607     this.rtlch  = [];
20608     this.content = [];
20609     this.cn = [];
20610     
20611 };
20612 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
20613     addChild : function(cn)
20614     {
20615         this.cn.push(cn);
20616         switch(cn.type) {
20617             case 'rtlch': // most content seems to be inside this??
20618             case 'listtext':
20619             case 'shpinst':
20620                 this.rtlch.push(cn);
20621                 return;
20622             default:
20623                 this[cn.type] = cn;
20624         }
20625         
20626     },
20627     
20628     getElementsByType : function(type)
20629     {
20630         var ret =  [];
20631         this._getElementsByType(type, ret, this.cn, 'rtf');
20632         return ret;
20633     },
20634     _getElementsByType : function (type, ret, search_array, path)
20635     {
20636         search_array.forEach(function(n,i) {
20637             if (n.type == type) {
20638                 n.path = path + '/' + n.type + ':' + i;
20639                 ret.push(n);
20640             }
20641             if (n.cn.length > 0) {
20642                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
20643             }
20644         },this);
20645     }
20646     
20647 });
20648  
20649 Roo.rtf.Ctrl = function(opts)
20650 {
20651     this.value = opts.value;
20652     this.param = opts.param;
20653 };
20654 /**
20655  *
20656  *
20657  * based on this https://github.com/iarna/rtf-parser
20658  * it's really only designed to extract pict from pasted RTF 
20659  *
20660  * usage:
20661  *
20662  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
20663  *  
20664  *
20665  */
20666
20667  
20668
20669
20670
20671 Roo.rtf.Parser = function(text) {
20672     //super({objectMode: true})
20673     this.text = '';
20674     this.parserState = this.parseText;
20675     
20676     // these are for interpeter...
20677     this.doc = {};
20678     ///this.parserState = this.parseTop
20679     this.groupStack = [];
20680     this.hexStore = [];
20681     this.doc = false;
20682     
20683     this.groups = []; // where we put the return.
20684     
20685     for (var ii = 0; ii < text.length; ++ii) {
20686         ++this.cpos;
20687         
20688         if (text[ii] === '\n') {
20689             ++this.row;
20690             this.col = 1;
20691         } else {
20692             ++this.col;
20693         }
20694         this.parserState(text[ii]);
20695     }
20696     
20697     
20698     
20699 };
20700 Roo.rtf.Parser.prototype = {
20701     text : '', // string being parsed..
20702     controlWord : '',
20703     controlWordParam :  '',
20704     hexChar : '',
20705     doc : false,
20706     group: false,
20707     groupStack : false,
20708     hexStore : false,
20709     
20710     
20711     cpos : 0, 
20712     row : 1, // reportin?
20713     col : 1, //
20714
20715      
20716     push : function (el)
20717     {
20718         var m = 'cmd'+ el.type;
20719         if (typeof(this[m]) == 'undefined') {
20720             Roo.log('invalid cmd:' + el.type);
20721             return;
20722         }
20723         this[m](el);
20724         //Roo.log(el);
20725     },
20726     flushHexStore : function()
20727     {
20728         if (this.hexStore.length < 1) {
20729             return;
20730         }
20731         var hexstr = this.hexStore.map(
20732             function(cmd) {
20733                 return cmd.value;
20734         }).join('');
20735         
20736         this.group.addContent( new Roo.rtf.Hex( hexstr ));
20737               
20738             
20739         this.hexStore.splice(0)
20740         
20741     },
20742     
20743     cmdgroupstart : function()
20744     {
20745         this.flushHexStore();
20746         if (this.group) {
20747             this.groupStack.push(this.group);
20748         }
20749          // parent..
20750         if (this.doc === false) {
20751             this.group = this.doc = new Roo.rtf.Document();
20752             return;
20753             
20754         }
20755         this.group = new Roo.rtf.Group(this.group);
20756     },
20757     cmdignorable : function()
20758     {
20759         this.flushHexStore();
20760         this.group.ignorable = true;
20761     },
20762     cmdendparagraph : function()
20763     {
20764         this.flushHexStore();
20765         this.group.addContent(new Roo.rtf.Paragraph());
20766     },
20767     cmdgroupend : function ()
20768     {
20769         this.flushHexStore();
20770         var endingGroup = this.group;
20771         
20772         
20773         this.group = this.groupStack.pop();
20774         if (this.group) {
20775             this.group.addChild(endingGroup);
20776         }
20777         
20778         
20779         
20780         var doc = this.group || this.doc;
20781         //if (endingGroup instanceof FontTable) {
20782         //  doc.fonts = endingGroup.table
20783         //} else if (endingGroup instanceof ColorTable) {
20784         //  doc.colors = endingGroup.table
20785         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
20786         if (endingGroup.ignorable === false) {
20787             //code
20788             this.groups.push(endingGroup);
20789            // Roo.log( endingGroup );
20790         }
20791             //Roo.each(endingGroup.content, function(item)) {
20792             //    doc.addContent(item);
20793             //}
20794             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
20795         //}
20796     },
20797     cmdtext : function (cmd)
20798     {
20799         this.flushHexStore();
20800         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
20801             //this.group = this.doc
20802         }
20803         this.group.addContent(new Roo.rtf.Span(cmd));
20804     },
20805     cmdcontrolword : function (cmd)
20806     {
20807         this.flushHexStore();
20808         if (!this.group.type) {
20809             this.group.type = cmd.value;
20810             return;
20811         }
20812         this.group.addContent(new Roo.rtf.Ctrl(cmd));
20813         // we actually don't care about ctrl words...
20814         return ;
20815         /*
20816         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
20817         if (this[method]) {
20818             this[method](cmd.param)
20819         } else {
20820             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
20821         }
20822         */
20823     },
20824     cmdhexchar : function(cmd) {
20825         this.hexStore.push(cmd);
20826     },
20827     cmderror : function(cmd) {
20828         throw new Exception (cmd.value);
20829     },
20830     
20831     /*
20832       _flush (done) {
20833         if (this.text !== '\u0000') this.emitText()
20834         done()
20835       }
20836       */
20837       
20838       
20839     parseText : function(c)
20840     {
20841         if (c === '\\') {
20842             this.parserState = this.parseEscapes;
20843         } else if (c === '{') {
20844             this.emitStartGroup();
20845         } else if (c === '}') {
20846             this.emitEndGroup();
20847         } else if (c === '\x0A' || c === '\x0D') {
20848             // cr/lf are noise chars
20849         } else {
20850             this.text += c;
20851         }
20852     },
20853     
20854     parseEscapes: function (c)
20855     {
20856         if (c === '\\' || c === '{' || c === '}') {
20857             this.text += c;
20858             this.parserState = this.parseText;
20859         } else {
20860             this.parserState = this.parseControlSymbol;
20861             this.parseControlSymbol(c);
20862         }
20863     },
20864     parseControlSymbol: function(c)
20865     {
20866         if (c === '~') {
20867             this.text += '\u00a0'; // nbsp
20868             this.parserState = this.parseText
20869         } else if (c === '-') {
20870              this.text += '\u00ad'; // soft hyphen
20871         } else if (c === '_') {
20872             this.text += '\u2011'; // non-breaking hyphen
20873         } else if (c === '*') {
20874             this.emitIgnorable();
20875             this.parserState = this.parseText;
20876         } else if (c === "'") {
20877             this.parserState = this.parseHexChar;
20878         } else if (c === '|') { // formula cacter
20879             this.emitFormula();
20880             this.parserState = this.parseText;
20881         } else if (c === ':') { // subentry in an index entry
20882             this.emitIndexSubEntry();
20883             this.parserState = this.parseText;
20884         } else if (c === '\x0a') {
20885             this.emitEndParagraph();
20886             this.parserState = this.parseText;
20887         } else if (c === '\x0d') {
20888             this.emitEndParagraph();
20889             this.parserState = this.parseText;
20890         } else {
20891             this.parserState = this.parseControlWord;
20892             this.parseControlWord(c);
20893         }
20894     },
20895     parseHexChar: function (c)
20896     {
20897         if (/^[A-Fa-f0-9]$/.test(c)) {
20898             this.hexChar += c;
20899             if (this.hexChar.length >= 2) {
20900               this.emitHexChar();
20901               this.parserState = this.parseText;
20902             }
20903             return;
20904         }
20905         this.emitError("Invalid character \"" + c + "\" in hex literal.");
20906         this.parserState = this.parseText;
20907         
20908     },
20909     parseControlWord : function(c)
20910     {
20911         if (c === ' ') {
20912             this.emitControlWord();
20913             this.parserState = this.parseText;
20914         } else if (/^[-\d]$/.test(c)) {
20915             this.parserState = this.parseControlWordParam;
20916             this.controlWordParam += c;
20917         } else if (/^[A-Za-z]$/.test(c)) {
20918           this.controlWord += c;
20919         } else {
20920           this.emitControlWord();
20921           this.parserState = this.parseText;
20922           this.parseText(c);
20923         }
20924     },
20925     parseControlWordParam : function (c) {
20926         if (/^\d$/.test(c)) {
20927           this.controlWordParam += c;
20928         } else if (c === ' ') {
20929           this.emitControlWord();
20930           this.parserState = this.parseText;
20931         } else {
20932           this.emitControlWord();
20933           this.parserState = this.parseText;
20934           this.parseText(c);
20935         }
20936     },
20937     
20938     
20939     
20940     
20941     emitText : function () {
20942         if (this.text === '') {
20943             return;
20944         }
20945         this.push({
20946             type: 'text',
20947             value: this.text,
20948             pos: this.cpos,
20949             row: this.row,
20950             col: this.col
20951         });
20952         this.text = ''
20953     },
20954     emitControlWord : function ()
20955     {
20956         this.emitText();
20957         if (this.controlWord === '') {
20958             this.emitError('empty control word');
20959         } else {
20960             this.push({
20961                   type: 'controlword',
20962                   value: this.controlWord,
20963                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
20964                   pos: this.cpos,
20965                   row: this.row,
20966                   col: this.col
20967             });
20968         }
20969         this.controlWord = '';
20970         this.controlWordParam = '';
20971     },
20972     emitStartGroup : function ()
20973     {
20974         this.emitText();
20975         this.push({
20976             type: 'groupstart',
20977             pos: this.cpos,
20978             row: this.row,
20979             col: this.col
20980         });
20981     },
20982     emitEndGroup : function ()
20983     {
20984         this.emitText();
20985         this.push({
20986             type: 'groupend',
20987             pos: this.cpos,
20988             row: this.row,
20989             col: this.col
20990         });
20991     },
20992     emitIgnorable : function ()
20993     {
20994         this.emitText();
20995         this.push({
20996             type: 'ignorable',
20997             pos: this.cpos,
20998             row: this.row,
20999             col: this.col
21000         });
21001     },
21002     emitHexChar : function ()
21003     {
21004         this.emitText();
21005         this.push({
21006             type: 'hexchar',
21007             value: this.hexChar,
21008             pos: this.cpos,
21009             row: this.row,
21010             col: this.col
21011         });
21012         this.hexChar = ''
21013     },
21014     emitError : function (message)
21015     {
21016       this.emitText();
21017       this.push({
21018             type: 'error',
21019             value: message,
21020             row: this.row,
21021             col: this.col,
21022             char: this.cpos //,
21023             //stack: new Error().stack
21024         });
21025     },
21026     emitEndParagraph : function () {
21027         this.emitText();
21028         this.push({
21029             type: 'endparagraph',
21030             pos: this.cpos,
21031             row: this.row,
21032             col: this.col
21033         });
21034     }
21035      
21036 } ;
21037 Roo.htmleditor = {};
21038  
21039 /**
21040  * @class Roo.htmleditor.Filter
21041  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
21042  * @cfg {DomElement} node The node to iterate and filter
21043  * @cfg {boolean|String|Array} tag Tags to replace 
21044  * @constructor
21045  * Create a new Filter.
21046  * @param {Object} config Configuration options
21047  */
21048
21049
21050
21051 Roo.htmleditor.Filter = function(cfg) {
21052     Roo.apply(this.cfg);
21053     // this does not actually call walk as it's really just a abstract class
21054 }
21055
21056
21057 Roo.htmleditor.Filter.prototype = {
21058     
21059     node: false,
21060     
21061     tag: false,
21062
21063     // overrride to do replace comments.
21064     replaceComment : false,
21065     
21066     // overrride to do replace or do stuff with tags..
21067     replaceTag : false,
21068     
21069     walk : function(dom)
21070     {
21071         Roo.each( Array.from(dom.childNodes), function( e ) {
21072             switch(true) {
21073                 
21074                 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
21075                     this.replaceComment(e);
21076                     return;
21077                 
21078                 case e.nodeType != 1: //not a node.
21079                     return;
21080                 
21081                 case this.tag === true: // everything
21082                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
21083                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
21084                     if (this.replaceTag && false === this.replaceTag(e)) {
21085                         return;
21086                     }
21087                     if (e.hasChildNodes()) {
21088                         this.walk(e);
21089                     }
21090                     return;
21091                 
21092                 default:    // tags .. that do not match.
21093                     if (e.hasChildNodes()) {
21094                         this.walk(e);
21095                     }
21096             }
21097             
21098         }, this);
21099         
21100     }
21101 }; 
21102
21103 /**
21104  * @class Roo.htmleditor.FilterAttributes
21105  * clean attributes and  styles including http:// etc.. in attribute
21106  * @constructor
21107 * Run a new Attribute Filter
21108 * @param {Object} config Configuration options
21109  */
21110 Roo.htmleditor.FilterAttributes = function(cfg)
21111 {
21112     Roo.apply(this, cfg);
21113     this.attrib_black = this.attrib_black || [];
21114     this.attrib_white = this.attrib_white || [];
21115
21116     this.attrib_clean = this.attrib_clean || [];
21117     this.style_white = this.style_white || [];
21118     this.style_black = this.style_black || [];
21119     this.walk(cfg.node);
21120 }
21121
21122 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
21123 {
21124     tag: true, // all tags
21125     
21126     attrib_black : false, // array
21127     attrib_clean : false,
21128     attrib_white : false,
21129
21130     style_white : false,
21131     style_black : false,
21132      
21133      
21134     replaceTag : function(node)
21135     {
21136         if (!node.attributes || !node.attributes.length) {
21137             return true;
21138         }
21139         
21140         for (var i = node.attributes.length-1; i > -1 ; i--) {
21141             var a = node.attributes[i];
21142             //console.log(a);
21143             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
21144                 node.removeAttribute(a.name);
21145                 continue;
21146             }
21147             
21148             
21149             
21150             if (a.name.toLowerCase().substr(0,2)=='on')  {
21151                 node.removeAttribute(a.name);
21152                 continue;
21153             }
21154             
21155             
21156             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
21157                 node.removeAttribute(a.name);
21158                 continue;
21159             }
21160             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
21161                 this.cleanAttr(node,a.name,a.value); // fixme..
21162                 continue;
21163             }
21164             if (a.name == 'style') {
21165                 this.cleanStyle(node,a.name,a.value);
21166                 continue;
21167             }
21168             /// clean up MS crap..
21169             // tecnically this should be a list of valid class'es..
21170             
21171             
21172             if (a.name == 'class') {
21173                 if (a.value.match(/^Mso/)) {
21174                     node.removeAttribute('class');
21175                 }
21176                 
21177                 if (a.value.match(/^body$/)) {
21178                     node.removeAttribute('class');
21179                 }
21180                 continue;
21181             }
21182             
21183             
21184             // style cleanup!?
21185             // class cleanup?
21186             
21187         }
21188         return true; // clean children
21189     },
21190         
21191     cleanAttr: function(node, n,v)
21192     {
21193         
21194         if (v.match(/^\./) || v.match(/^\//)) {
21195             return;
21196         }
21197         if (v.match(/^(http|https):\/\//)
21198             || v.match(/^mailto:/) 
21199             || v.match(/^ftp:/)
21200             || v.match(/^data:/)
21201             ) {
21202             return;
21203         }
21204         if (v.match(/^#/)) {
21205             return;
21206         }
21207         if (v.match(/^\{/)) { // allow template editing.
21208             return;
21209         }
21210 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21211         node.removeAttribute(n);
21212         
21213     },
21214     cleanStyle : function(node,  n,v)
21215     {
21216         if (v.match(/expression/)) { //XSS?? should we even bother..
21217             node.removeAttribute(n);
21218             return;
21219         }
21220         
21221         var parts = v.split(/;/);
21222         var clean = [];
21223         
21224         Roo.each(parts, function(p) {
21225             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21226             if (!p.length) {
21227                 return true;
21228             }
21229             var l = p.split(':').shift().replace(/\s+/g,'');
21230             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21231             
21232             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
21233                 return true;
21234             }
21235             //Roo.log()
21236             // only allow 'c whitelisted system attributes'
21237             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
21238                 return true;
21239             }
21240             
21241             
21242             clean.push(p);
21243             return true;
21244         },this);
21245         if (clean.length) { 
21246             node.setAttribute(n, clean.join(';'));
21247         } else {
21248             node.removeAttribute(n);
21249         }
21250         
21251     }
21252         
21253         
21254         
21255     
21256 });/**
21257  * @class Roo.htmleditor.FilterBlack
21258  * remove blacklisted elements.
21259  * @constructor
21260  * Run a new Blacklisted Filter
21261  * @param {Object} config Configuration options
21262  */
21263
21264 Roo.htmleditor.FilterBlack = function(cfg)
21265 {
21266     Roo.apply(this, cfg);
21267     this.walk(cfg.node);
21268 }
21269
21270 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
21271 {
21272     tag : true, // all elements.
21273    
21274     replace : function(n)
21275     {
21276         n.parentNode.removeChild(n);
21277     }
21278 });
21279 /**
21280  * @class Roo.htmleditor.FilterComment
21281  * remove comments.
21282  * @constructor
21283 * Run a new Comments Filter
21284 * @param {Object} config Configuration options
21285  */
21286 Roo.htmleditor.FilterComment = function(cfg)
21287 {
21288     this.walk(cfg.node);
21289 }
21290
21291 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
21292 {
21293   
21294     replaceComment : function(n)
21295     {
21296         n.parentNode.removeChild(n);
21297     }
21298 });/**
21299  * @class Roo.htmleditor.FilterKeepChildren
21300  * remove tags but keep children
21301  * @constructor
21302  * Run a new Keep Children Filter
21303  * @param {Object} config Configuration options
21304  */
21305
21306 Roo.htmleditor.FilterKeepChildren = function(cfg)
21307 {
21308     Roo.apply(this, cfg);
21309     if (this.tag === false) {
21310         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
21311     }
21312     this.walk(cfg.node);
21313 }
21314
21315 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
21316 {
21317     
21318   
21319     replaceTag : function(node)
21320     {
21321         // walk children...
21322         //Roo.log(node);
21323         var ar = Array.from(node.childNodes);
21324         //remove first..
21325         for (var i = 0; i < ar.length; i++) {
21326             if (ar[i].nodeType == 1) {
21327                 if (
21328                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
21329                     || // array and it matches
21330                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
21331                 ) {
21332                     this.replaceTag(ar[i]); // child is blacklisted as well...
21333                     continue;
21334                 }
21335             }
21336         }  
21337         ar = Array.from(node.childNodes);
21338         for (var i = 0; i < ar.length; i++) {
21339          
21340             node.removeChild(ar[i]);
21341             // what if we need to walk these???
21342             node.parentNode.insertBefore(ar[i], node);
21343             if (this.tag !== false) {
21344                 this.walk(ar[i]);
21345                 
21346             }
21347         }
21348         node.parentNode.removeChild(node);
21349         return false; // don't walk children
21350         
21351         
21352     }
21353 });/**
21354  * @class Roo.htmleditor.FilterParagraph
21355  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
21356  * like on 'push' to remove the <p> tags and replace them with line breaks.
21357  * @constructor
21358  * Run a new Paragraph Filter
21359  * @param {Object} config Configuration options
21360  */
21361
21362 Roo.htmleditor.FilterParagraph = function(cfg)
21363 {
21364     // no need to apply config.
21365     this.walk(cfg.node);
21366 }
21367
21368 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
21369 {
21370     
21371      
21372     tag : 'P',
21373     
21374      
21375     replaceTag : function(node)
21376     {
21377         
21378         if (node.childNodes.length == 1 &&
21379             node.childNodes[0].nodeType == 3 &&
21380             node.childNodes[0].textContent.trim().length < 1
21381             ) {
21382             // remove and replace with '<BR>';
21383             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
21384             return false; // no need to walk..
21385         }
21386         var ar = Array.from(node.childNodes);
21387         for (var i = 0; i < ar.length; i++) {
21388             node.removeChild(ar[i]);
21389             // what if we need to walk these???
21390             node.parentNode.insertBefore(ar[i], node);
21391         }
21392         // now what about this?
21393         // <p> &nbsp; </p>
21394         
21395         // double BR.
21396         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21397         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21398         node.parentNode.removeChild(node);
21399         
21400         return false;
21401
21402     }
21403     
21404 });/**
21405  * @class Roo.htmleditor.FilterSpan
21406  * filter span's with no attributes out..
21407  * @constructor
21408  * Run a new Span Filter
21409  * @param {Object} config Configuration options
21410  */
21411
21412 Roo.htmleditor.FilterSpan = function(cfg)
21413 {
21414     // no need to apply config.
21415     this.walk(cfg.node);
21416 }
21417
21418 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
21419 {
21420      
21421     tag : 'SPAN',
21422      
21423  
21424     replaceTag : function(node)
21425     {
21426         if (node.attributes && node.attributes.length > 0) {
21427             return true; // walk if there are any.
21428         }
21429         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
21430         return false;
21431      
21432     }
21433     
21434 });/**
21435  * @class Roo.htmleditor.FilterTableWidth
21436   try and remove table width data - as that frequently messes up other stuff.
21437  * 
21438  *      was cleanTableWidths.
21439  *
21440  * Quite often pasting from word etc.. results in tables with column and widths.
21441  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21442  *
21443  * @constructor
21444  * Run a new Table Filter
21445  * @param {Object} config Configuration options
21446  */
21447
21448 Roo.htmleditor.FilterTableWidth = function(cfg)
21449 {
21450     // no need to apply config.
21451     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
21452     this.walk(cfg.node);
21453 }
21454
21455 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
21456 {
21457      
21458      
21459     
21460     replaceTag: function(node) {
21461         
21462         
21463       
21464         if (node.hasAttribute('width')) {
21465             node.removeAttribute('width');
21466         }
21467         
21468          
21469         if (node.hasAttribute("style")) {
21470             // pretty basic...
21471             
21472             var styles = node.getAttribute("style").split(";");
21473             var nstyle = [];
21474             Roo.each(styles, function(s) {
21475                 if (!s.match(/:/)) {
21476                     return;
21477                 }
21478                 var kv = s.split(":");
21479                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21480                     return;
21481                 }
21482                 // what ever is left... we allow.
21483                 nstyle.push(s);
21484             });
21485             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21486             if (!nstyle.length) {
21487                 node.removeAttribute('style');
21488             }
21489         }
21490         
21491         return true; // continue doing children..
21492     }
21493 });/**
21494  * @class Roo.htmleditor.FilterWord
21495  * try and clean up all the mess that Word generates.
21496  * 
21497  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
21498  
21499  * @constructor
21500  * Run a new Span Filter
21501  * @param {Object} config Configuration options
21502  */
21503
21504 Roo.htmleditor.FilterWord = function(cfg)
21505 {
21506     // no need to apply config.
21507     this.walk(cfg.node);
21508 }
21509
21510 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
21511 {
21512     tag: true,
21513      
21514     
21515     /**
21516      * Clean up MS wordisms...
21517      */
21518     replaceTag : function(node)
21519     {
21520          
21521         // no idea what this does - span with text, replaceds with just text.
21522         if(
21523                 node.nodeName == 'SPAN' &&
21524                 !node.hasAttributes() &&
21525                 node.childNodes.length == 1 &&
21526                 node.firstChild.nodeName == "#text"  
21527         ) {
21528             var textNode = node.firstChild;
21529             node.removeChild(textNode);
21530             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21531                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21532             }
21533             node.parentNode.insertBefore(textNode, node);
21534             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21535                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21536             }
21537             
21538             node.parentNode.removeChild(node);
21539             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
21540         }
21541         
21542    
21543         
21544         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21545             node.parentNode.removeChild(node);
21546             return false; // dont do chidlren
21547         }
21548         //Roo.log(node.tagName);
21549         // remove - but keep children..
21550         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21551             //Roo.log('-- removed');
21552             while (node.childNodes.length) {
21553                 var cn = node.childNodes[0];
21554                 node.removeChild(cn);
21555                 node.parentNode.insertBefore(cn, node);
21556                 // move node to parent - and clean it..
21557                 this.replaceTag(cn);
21558             }
21559             node.parentNode.removeChild(node);
21560             /// no need to iterate chidlren = it's got none..
21561             //this.iterateChildren(node, this.cleanWord);
21562             return false; // no need to iterate children.
21563         }
21564         // clean styles
21565         if (node.className.length) {
21566             
21567             var cn = node.className.split(/\W+/);
21568             var cna = [];
21569             Roo.each(cn, function(cls) {
21570                 if (cls.match(/Mso[a-zA-Z]+/)) {
21571                     return;
21572                 }
21573                 cna.push(cls);
21574             });
21575             node.className = cna.length ? cna.join(' ') : '';
21576             if (!cna.length) {
21577                 node.removeAttribute("class");
21578             }
21579         }
21580         
21581         if (node.hasAttribute("lang")) {
21582             node.removeAttribute("lang");
21583         }
21584         
21585         if (node.hasAttribute("style")) {
21586             
21587             var styles = node.getAttribute("style").split(";");
21588             var nstyle = [];
21589             Roo.each(styles, function(s) {
21590                 if (!s.match(/:/)) {
21591                     return;
21592                 }
21593                 var kv = s.split(":");
21594                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21595                     return;
21596                 }
21597                 // what ever is left... we allow.
21598                 nstyle.push(s);
21599             });
21600             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21601             if (!nstyle.length) {
21602                 node.removeAttribute('style');
21603             }
21604         }
21605         return true; // do children
21606         
21607         
21608         
21609     }
21610 });
21611 /**
21612  * @class Roo.htmleditor.FilterStyleToTag
21613  * part of the word stuff... - certain 'styles' should be converted to tags.
21614  * eg.
21615  *   font-weight: bold -> bold
21616  *   ?? super / subscrit etc..
21617  * 
21618  * @constructor
21619 * Run a new style to tag filter.
21620 * @param {Object} config Configuration options
21621  */
21622 Roo.htmleditor.FilterStyleToTag = function(cfg)
21623 {
21624     
21625     this.tags = {
21626         B  : [ 'fontWeight' , 'bold'],
21627         I :  [ 'fontStyle' , 'italic'],
21628         //pre :  [ 'font-style' , 'italic'],
21629         // h1.. h6 ?? font-size?
21630         SUP : [ 'verticalAlign' , 'super' ],
21631         SUB : [ 'verticalAlign' , 'sub' ]
21632         
21633         
21634     };
21635     
21636     Roo.apply(this, cfg);
21637      
21638     
21639     this.walk(cfg.node);
21640     
21641     
21642     
21643 }
21644
21645
21646 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
21647 {
21648     tag: true, // all tags
21649     
21650     tags : false,
21651     
21652     
21653     replaceTag : function(node)
21654     {
21655         
21656         
21657         if (node.getAttribute("style") === null) {
21658             return true;
21659         }
21660         var inject = [];
21661         for (var k in this.tags) {
21662             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
21663                 inject.push(k);
21664                 node.style.removeProperty(this.tags[k][0]);
21665             }
21666         }
21667         if (!inject.length) {
21668             return true; 
21669         }
21670         var cn = Array.from(node.childNodes);
21671         var nn = node;
21672         Roo.each(inject, function(t) {
21673             var nc = node.ownerDocument.createelement(t);
21674             nn.appendChild(nc);
21675             nn = nc;
21676         });
21677         for(var i = 0;i < cn.length;cn++) {
21678             node.removeChild(cn[i]);
21679             nn.appendChild(cn[i]);
21680         }
21681         return true /// iterate thru
21682     }
21683     
21684 })/**
21685  * @class Roo.htmleditor.FilterLongBr
21686  * BR/BR/BR - keep a maximum of 2...
21687  * @constructor
21688  * Run a new Long BR Filter
21689  * @param {Object} config Configuration options
21690  */
21691
21692 Roo.htmleditor.FilterLongBr = function(cfg)
21693 {
21694     // no need to apply config.
21695     this.walk(cfg.node);
21696 }
21697
21698 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
21699 {
21700     
21701      
21702     tag : 'BR',
21703     
21704      
21705     replaceTag : function(node)
21706     {
21707         
21708         var ps = node.nextSibling;
21709         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21710             ps = ps.nextSibling;
21711         }
21712         
21713         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
21714             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
21715             return false;
21716         }
21717         
21718         if (!ps || ps.nodeType != 1) {
21719             return false;
21720         }
21721         
21722         if (!ps || ps.tagName != 'BR') {
21723            
21724             return false;
21725         }
21726         
21727         
21728         
21729         
21730         
21731         if (!node.previousSibling) {
21732             return false;
21733         }
21734         var ps = node.previousSibling;
21735         
21736         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21737             ps = ps.previousSibling;
21738         }
21739         if (!ps || ps.nodeType != 1) {
21740             return false;
21741         }
21742         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
21743         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
21744             return false;
21745         }
21746         
21747         node.parentNode.removeChild(node); // remove me...
21748         
21749         return false; // no need to do children
21750
21751     }
21752     
21753 });
21754 /**
21755  * @class Roo.htmleditor.Tidy
21756  * Tidy HTML 
21757  * @cfg {Roo.HtmlEditorCore} core the editor.
21758  * @constructor
21759  * Create a new Filter.
21760  * @param {Object} config Configuration options
21761  */
21762
21763
21764 Roo.htmleditor.Tidy = function(cfg) {
21765     Roo.apply(this, cfg);
21766     
21767     this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
21768      
21769 }
21770
21771 Roo.htmleditor.Tidy.toString = function(node)
21772 {
21773     return Roo.htmleditor.Tidy.prototype.tidy(node, '');
21774 }
21775
21776 Roo.htmleditor.Tidy.prototype = {
21777     
21778     
21779     wrap : function(s) {
21780         return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
21781     },
21782
21783     
21784     tidy : function(node, indent) {
21785      
21786         if  (node.nodeType == 3) {
21787             // text.
21788             
21789             
21790             return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
21791                 
21792             
21793         }
21794         
21795         if  (node.nodeType != 1) {
21796             return '';
21797         }
21798         
21799         
21800         
21801         if (node.tagName == 'BODY') {
21802             
21803             return this.cn(node, '');
21804         }
21805              
21806              // Prints the node tagName, such as <A>, <IMG>, etc
21807         var ret = "<" + node.tagName +  this.attr(node) ;
21808         
21809         // elements with no children..
21810         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) {
21811                 return ret + '/>';
21812         }
21813         ret += '>';
21814         
21815         
21816         var cindent = indent === false ? '' : (indent + '  ');
21817         // tags where we will not pad the children.. (inline text tags etc..)
21818         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code?
21819             cindent = false;
21820             
21821             
21822         }
21823         
21824         var cn = this.cn(node, cindent );
21825         
21826         return ret + cn  + '</' + node.tagName + '>';
21827         
21828     },
21829     cn: function(node, indent)
21830     {
21831         var ret = [];
21832         
21833         var ar = Array.from(node.childNodes);
21834         for (var i = 0 ; i < ar.length ; i++) {
21835             
21836             
21837             
21838             if (indent !== false   // indent==false preservies everything
21839                 && i > 0
21840                 && ar[i].nodeType == 3 
21841                 && ar[i].nodeValue.length > 0
21842                 && ar[i].nodeValue.match(/^\s+/)
21843             ) {
21844                 if (ret.length && ret[ret.length-1] == "\n" + indent) {
21845                     ret.pop(); // remove line break from last?
21846                 }
21847                 
21848                 ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces.
21849             }
21850             if (indent !== false
21851                 && ar[i].nodeType == 1 // element - and indent is not set... 
21852             ) {
21853                 ret.push("\n" + indent); 
21854             }
21855             
21856             ret.push(this.tidy(ar[i], indent));
21857             // text + trailing indent 
21858             if (indent !== false
21859                 && ar[i].nodeType == 3
21860                 && ar[i].nodeValue.length > 0
21861                 && ar[i].nodeValue.match(/\s+$/)
21862             ){
21863                 ret.push("\n" + indent); 
21864             }
21865             
21866             
21867             
21868             
21869         }
21870         // what if all text?
21871         
21872         
21873         return ret.join('');
21874     },
21875     
21876          
21877         
21878     attr : function(node)
21879     {
21880         var attr = [];
21881         for(i = 0; i < node.attributes.length;i++) {
21882             
21883             // skip empty values?
21884             if (!node.attributes.item(i).value.length) {
21885                 continue;
21886             }
21887             attr.push(  node.attributes.item(i).name + '="' +
21888                     Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"'
21889             );
21890         }
21891         return attr.length ? (' ' + attr.join(' ') ) : '';
21892         
21893     }
21894     
21895     
21896     
21897 }
21898 /**
21899  * @class Roo.htmleditor.KeyEnter
21900  * Handle Enter press..
21901  * @cfg {Roo.HtmlEditorCore} core the editor.
21902  * @constructor
21903  * Create a new Filter.
21904  * @param {Object} config Configuration options
21905  */
21906
21907
21908
21909 Roo.htmleditor.KeyEnter = function(cfg) {
21910     Roo.apply(this, cfg);
21911     // this does not actually call walk as it's really just a abstract class
21912  
21913     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
21914 }
21915
21916
21917 Roo.htmleditor.KeyEnter.prototype = {
21918     
21919     core : false,
21920     
21921     keypress : function(e) {
21922         if (e.charCode != 13) {
21923             return true;
21924         }
21925         e.preventDefault();
21926         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
21927         var doc = this.core.doc;
21928         
21929         var docFragment = doc.createDocumentFragment();
21930     
21931         //add a new line
21932         var newEle = doc.createTextNode('\n');
21933         docFragment.appendChild(newEle);
21934     
21935     
21936         var range = this.core.win.getSelection().getRangeAt(0);
21937         var n = range.commonAncestorContainer ;
21938         while (n && n.nodeType != 1) {
21939             n  = n.parentNode;
21940         }
21941         var li = false;
21942         if (n && n.tagName == 'UL') {
21943             li = doc.createElement('LI');
21944             n.appendChild(li);
21945             
21946         }
21947         if (n && n.tagName == 'LI') {
21948             li = doc.createElement('LI');
21949             if (n.nextSibling) {
21950                 n.parentNode.insertBefore(li, n.firstSibling);
21951                 
21952             } else {
21953                 n.parentNode.appendChild(li);
21954             }
21955         }
21956         if (li) {   
21957             range = doc.createRange();
21958             range.setStartAfter(li);
21959             range.collapse(true);
21960         
21961             //make the cursor there
21962             var sel = this.core.win.getSelection();
21963             sel.removeAllRanges();
21964             sel.addRange(range);
21965             return false;
21966             
21967             
21968         }
21969         //add the br, or p, or something else
21970         newEle = doc.createElement('br');
21971         docFragment.appendChild(newEle);
21972     
21973         //make the br replace selection
21974         
21975         range.deleteContents();
21976         
21977         range.insertNode(docFragment);
21978     
21979         //create a new range
21980         range = doc.createRange();
21981         range.setStartAfter(newEle);
21982         range.collapse(true);
21983     
21984         //make the cursor there
21985         var sel = this.core.win.getSelection();
21986         sel.removeAllRanges();
21987         sel.addRange(range);
21988     
21989         return false;
21990          
21991     }
21992 };
21993      
21994 /**
21995  * @class Roo.htmleditor.Block
21996  * Base class for html editor blocks - do not use it directly .. extend it..
21997  * @cfg {DomElement} node The node to apply stuff to.
21998  * @cfg {String} friendly_name the name that appears in the context bar about this block
21999  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
22000  
22001  * @constructor
22002  * Create a new Filter.
22003  * @param {Object} config Configuration options
22004  */
22005
22006 Roo.htmleditor.Block  = function(cfg)
22007 {
22008     // do nothing .. should not be called really.
22009 }
22010
22011 Roo.htmleditor.Block.factory = function(node)
22012 {
22013     
22014     var id = Roo.get(node).id;
22015     if (typeof(Roo.htmleditor.Block.cache[id]) != 'undefined') {
22016         return Roo.htmleditor.Block.cache[id];
22017     }
22018     
22019     var cls = Roo.htmleditor['Block' + Roo.get(node).attr('data-block')];
22020     if (typeof(cls) == 'undefined') {
22021         Roo.log("OOps missing block : " + 'Block' + Roo.get(node).attr('data-block'));
22022         return false;
22023     }
22024     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
22025     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
22026 };
22027 // question goes here... do we need to clear out this cache sometimes?
22028 // or show we make it relivant to the htmleditor.
22029 Roo.htmleditor.Block.cache = {};
22030
22031 Roo.htmleditor.Block.prototype = {
22032     
22033      // used by context menu
22034     friendly_name : 'Image with caption',
22035     
22036     context : false,
22037     /**
22038      * Update a node with values from this object
22039      * @param {DomElement} node
22040      */
22041     updateElement : function(node)
22042     {
22043         Roo.DomHelper.update(node, this.toObject());
22044     },
22045      /**
22046      * convert to plain HTML for calling insertAtCursor..
22047      */
22048     toHTML : function()
22049     {
22050         return Roo.DomHelper.markup(this.toObject());
22051     },
22052     /**
22053      * used by readEleemnt to extract data from a node
22054      * may need improving as it's pretty basic
22055      
22056      * @param {DomElement} node
22057      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
22058      * @param {String} attribute (use html - for contents, or style for using next param as style)
22059      * @param {String} style the style property - eg. text-align
22060      */
22061     getVal : function(node, tag, attr, style)
22062     {
22063         var n = node;
22064         if (tag !== true && n.tagName != tag.toUpperCase()) {
22065             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
22066             // but kiss for now.
22067             n = node.getElementsByTagName(tag).item(0);
22068         }
22069         if (attr == 'html') {
22070             return n.innerHTML;
22071         }
22072         if (attr == 'style') {
22073             return Roo.get(n).getStyle(style);
22074         }
22075         
22076         return Roo.get(n).attr(attr);
22077             
22078     },
22079     /**
22080      * create a DomHelper friendly object - for use with 
22081      * Roo.DomHelper.markup / overwrite / etc..
22082      * (override this)
22083      */
22084     toObject : function()
22085     {
22086         return {};
22087     },
22088       /**
22089      * Read a node that has a 'data-block' property - and extract the values from it.
22090      * @param {DomElement} node - the node
22091      */
22092     readElement : function(node)
22093     {
22094         
22095     } 
22096     
22097     
22098 };
22099
22100  
22101
22102 /**
22103  * @class Roo.htmleditor.BlockFigure
22104  * Block that has an image and a figcaption
22105  * @cfg {String} image_src the url for the image
22106  * @cfg {String} align (left|right) alignment for the block default left
22107  * @cfg {String} text_align (left|right) alignment for the text caption default left.
22108  * @cfg {String} caption the text to appear below  (and in the alt tag)
22109  * @cfg {String|number} image_width the width of the image number or %?
22110  * @cfg {String|number} image_height the height of the image number or %?
22111  * 
22112  * @constructor
22113  * Create a new Filter.
22114  * @param {Object} config Configuration options
22115  */
22116
22117 Roo.htmleditor.BlockFigure = function(cfg)
22118 {
22119     if (cfg.node) {
22120         this.readElement(cfg.node);
22121         this.updateElement(cfg.node);
22122     }
22123     Roo.apply(this, cfg);
22124 }
22125 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
22126  
22127     
22128     // setable values.
22129     image_src: '',
22130     
22131     align: 'left',
22132     caption : '',
22133     text_align: 'left',
22134     
22135     width : '46%',
22136     margin: '2%',
22137     
22138     // used by context menu
22139     friendly_name : 'Image with caption',
22140     
22141     context : { // ?? static really
22142         width : {
22143             title: "Width",
22144             width: 40
22145             // ?? number
22146         },
22147         margin : {
22148             title: "Margin",
22149             width: 40
22150             // ?? number
22151         },
22152         align: {
22153             title: "Align",
22154             opts : [[ "left"],[ "right"]],
22155             width : 80
22156             
22157         },
22158         text_align: {
22159             title: "Caption Align",
22160             opts : [ [ "left"],[ "right"],[ "center"]],
22161             width : 80
22162         },
22163         
22164        
22165         image_src : {
22166             title: "Src",
22167             width: 220
22168         }
22169     },
22170     /**
22171      * create a DomHelper friendly object - for use with
22172      * Roo.DomHelper.markup / overwrite / etc..
22173      */
22174     toObject : function()
22175     {
22176         var d = document.createElement('div');
22177         d.innerHTML = this.caption;
22178         
22179         return {
22180             tag: 'figure',
22181             'data-block' : 'Figure',
22182             contenteditable : 'false',
22183             style : {
22184                 display: 'table',
22185                 float :  this.align ,
22186                 width :  this.width,
22187                 margin:  this.margin
22188             },
22189             cn : [
22190                 {
22191                     tag : 'img',
22192                     src : this.image_src,
22193                     alt : d.innerText.replace(/\n/g, " "), // removeHTML..
22194                     style: {
22195                         width: '100%'
22196                     }
22197                 },
22198                 {
22199                     tag: 'figcaption',
22200                     contenteditable : true,
22201                     style : {
22202                         'text-align': this.text_align
22203                     },
22204                     html : this.caption
22205                     
22206                 }
22207             ]
22208         };
22209     },
22210     
22211     readElement : function(node)
22212     {
22213         this.image_src = this.getVal(node, 'img', 'src');
22214         this.align = this.getVal(node, 'figure', 'style', 'float');
22215         this.caption = this.getVal(node, 'figcaption', 'html');
22216         this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
22217         this.width = this.getVal(node, 'figure', 'style', 'width');
22218         this.margin = this.getVal(node, 'figure', 'style', 'margin');
22219         
22220     } 
22221     
22222   
22223    
22224      
22225     
22226     
22227     
22228     
22229 })
22230
22231 //<script type="text/javascript">
22232
22233 /*
22234  * Based  Ext JS Library 1.1.1
22235  * Copyright(c) 2006-2007, Ext JS, LLC.
22236  * LGPL
22237  *
22238  */
22239  
22240 /**
22241  * @class Roo.HtmlEditorCore
22242  * @extends Roo.Component
22243  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22244  *
22245  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22246  */
22247
22248 Roo.HtmlEditorCore = function(config){
22249     
22250     
22251     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22252     
22253     
22254     this.addEvents({
22255         /**
22256          * @event initialize
22257          * Fires when the editor is fully initialized (including the iframe)
22258          * @param {Roo.HtmlEditorCore} this
22259          */
22260         initialize: true,
22261         /**
22262          * @event activate
22263          * Fires when the editor is first receives the focus. Any insertion must wait
22264          * until after this event.
22265          * @param {Roo.HtmlEditorCore} this
22266          */
22267         activate: true,
22268          /**
22269          * @event beforesync
22270          * Fires before the textarea is updated with content from the editor iframe. Return false
22271          * to cancel the sync.
22272          * @param {Roo.HtmlEditorCore} this
22273          * @param {String} html
22274          */
22275         beforesync: true,
22276          /**
22277          * @event beforepush
22278          * Fires before the iframe editor is updated with content from the textarea. Return false
22279          * to cancel the push.
22280          * @param {Roo.HtmlEditorCore} this
22281          * @param {String} html
22282          */
22283         beforepush: true,
22284          /**
22285          * @event sync
22286          * Fires when the textarea is updated with content from the editor iframe.
22287          * @param {Roo.HtmlEditorCore} this
22288          * @param {String} html
22289          */
22290         sync: true,
22291          /**
22292          * @event push
22293          * Fires when the iframe editor is updated with content from the textarea.
22294          * @param {Roo.HtmlEditorCore} this
22295          * @param {String} html
22296          */
22297         push: true,
22298         
22299         /**
22300          * @event editorevent
22301          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22302          * @param {Roo.HtmlEditorCore} this
22303          */
22304         editorevent: true
22305         
22306     });
22307     
22308     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22309     
22310     // defaults : white / black...
22311     this.applyBlacklists();
22312     
22313     
22314     
22315 };
22316
22317
22318 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22319
22320
22321      /**
22322      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22323      */
22324     
22325     owner : false,
22326     
22327      /**
22328      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22329      *                        Roo.resizable.
22330      */
22331     resizable : false,
22332      /**
22333      * @cfg {Number} height (in pixels)
22334      */   
22335     height: 300,
22336    /**
22337      * @cfg {Number} width (in pixels)
22338      */   
22339     width: 500,
22340     
22341     /**
22342      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22343      * 
22344      */
22345     stylesheets: false,
22346     
22347     /**
22348      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
22349      */
22350     allowComments: false,
22351     // id of frame..
22352     frameId: false,
22353     
22354     // private properties
22355     validationEvent : false,
22356     deferHeight: true,
22357     initialized : false,
22358     activated : false,
22359     sourceEditMode : false,
22360     onFocus : Roo.emptyFn,
22361     iframePad:3,
22362     hideMode:'offsets',
22363     
22364     clearUp: true,
22365     
22366     // blacklist + whitelisted elements..
22367     black: false,
22368     white: false,
22369      
22370     bodyCls : '',
22371
22372     /**
22373      * Protected method that will not generally be called directly. It
22374      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22375      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22376      */
22377     getDocMarkup : function(){
22378         // body styles..
22379         var st = '';
22380         
22381         // inherit styels from page...?? 
22382         if (this.stylesheets === false) {
22383             
22384             Roo.get(document.head).select('style').each(function(node) {
22385                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22386             });
22387             
22388             Roo.get(document.head).select('link').each(function(node) { 
22389                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22390             });
22391             
22392         } else if (!this.stylesheets.length) {
22393                 // simple..
22394                 st = '<style type="text/css">' +
22395                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22396                    '</style>';
22397         } else {
22398             for (var i in this.stylesheets) {
22399                 if (typeof(this.stylesheets[i]) != 'string') {
22400                     continue;
22401                 }
22402                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
22403             }
22404             
22405         }
22406         
22407         st +=  '<style type="text/css">' +
22408             'IMG { cursor: pointer } ' +
22409         '</style>';
22410
22411         var cls = 'roo-htmleditor-body';
22412         
22413         if(this.bodyCls.length){
22414             cls += ' ' + this.bodyCls;
22415         }
22416         
22417         return '<html><head>' + st  +
22418             //<style type="text/css">' +
22419             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22420             //'</style>' +
22421             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
22422     },
22423
22424     // private
22425     onRender : function(ct, position)
22426     {
22427         var _t = this;
22428         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22429         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22430         
22431         
22432         this.el.dom.style.border = '0 none';
22433         this.el.dom.setAttribute('tabIndex', -1);
22434         this.el.addClass('x-hidden hide');
22435         
22436         
22437         
22438         if(Roo.isIE){ // fix IE 1px bogus margin
22439             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22440         }
22441        
22442         
22443         this.frameId = Roo.id();
22444         
22445          
22446         
22447         var iframe = this.owner.wrap.createChild({
22448             tag: 'iframe',
22449             cls: 'form-control', // bootstrap..
22450             id: this.frameId,
22451             name: this.frameId,
22452             frameBorder : 'no',
22453             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22454         }, this.el
22455         );
22456         
22457         
22458         this.iframe = iframe.dom;
22459
22460         this.assignDocWin();
22461         
22462         this.doc.designMode = 'on';
22463        
22464         this.doc.open();
22465         this.doc.write(this.getDocMarkup());
22466         this.doc.close();
22467
22468         
22469         var task = { // must defer to wait for browser to be ready
22470             run : function(){
22471                 //console.log("run task?" + this.doc.readyState);
22472                 this.assignDocWin();
22473                 if(this.doc.body || this.doc.readyState == 'complete'){
22474                     try {
22475                         this.doc.designMode="on";
22476                     } catch (e) {
22477                         return;
22478                     }
22479                     Roo.TaskMgr.stop(task);
22480                     this.initEditor.defer(10, this);
22481                 }
22482             },
22483             interval : 10,
22484             duration: 10000,
22485             scope: this
22486         };
22487         Roo.TaskMgr.start(task);
22488
22489     },
22490
22491     // private
22492     onResize : function(w, h)
22493     {
22494          Roo.log('resize: ' +w + ',' + h );
22495         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22496         if(!this.iframe){
22497             return;
22498         }
22499         if(typeof w == 'number'){
22500             
22501             this.iframe.style.width = w + 'px';
22502         }
22503         if(typeof h == 'number'){
22504             
22505             this.iframe.style.height = h + 'px';
22506             if(this.doc){
22507                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22508             }
22509         }
22510         
22511     },
22512
22513     /**
22514      * Toggles the editor between standard and source edit mode.
22515      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22516      */
22517     toggleSourceEdit : function(sourceEditMode){
22518         
22519         this.sourceEditMode = sourceEditMode === true;
22520         
22521         if(this.sourceEditMode){
22522  
22523             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
22524             
22525         }else{
22526             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
22527             //this.iframe.className = '';
22528             this.deferFocus();
22529         }
22530         //this.setSize(this.owner.wrap.getSize());
22531         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22532     },
22533
22534     
22535   
22536
22537     /**
22538      * Protected method that will not generally be called directly. If you need/want
22539      * custom HTML cleanup, this is the method you should override.
22540      * @param {String} html The HTML to be cleaned
22541      * return {String} The cleaned HTML
22542      */
22543     cleanHtml : function(html){
22544         html = String(html);
22545         if(html.length > 5){
22546             if(Roo.isSafari){ // strip safari nonsense
22547                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22548             }
22549         }
22550         if(html == '&nbsp;'){
22551             html = '';
22552         }
22553         return html;
22554     },
22555
22556     /**
22557      * HTML Editor -> Textarea
22558      * Protected method that will not generally be called directly. Syncs the contents
22559      * of the editor iframe with the textarea.
22560      */
22561     syncValue : function()
22562     {
22563         Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
22564         if(this.initialized){
22565             var bd = (this.doc.body || this.doc.documentElement);
22566             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22567             
22568             // not sure if this is really the place for this
22569             // the blocks are synced occasionaly - since we currently dont add listeners on the blocks
22570             // this has to update attributes that get duped.. like alt and caption..
22571             
22572             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
22573                  Roo.htmleditor.Block.factory(e);
22574             },this);
22575             
22576             
22577             var div = document.createElement('div');
22578             div.innerHTML = bd.innerHTML;
22579             // remove content editable. (blocks)
22580             
22581            
22582             
22583             new Roo.htmleditor.FilterAttributes({node : div, attrib_black: [ 'contenteditable' ] });
22584             //?? tidy?
22585             var html = div.innerHTML;
22586             if(Roo.isSafari){
22587                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22588                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22589                 if(m && m[1]){
22590                     html = '<div style="'+m[0]+'">' + html + '</div>';
22591                 }
22592             }
22593             html = this.cleanHtml(html);
22594             // fix up the special chars.. normaly like back quotes in word...
22595             // however we do not want to do this with chinese..
22596             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22597                 
22598                 var cc = match.charCodeAt();
22599
22600                 // Get the character value, handling surrogate pairs
22601                 if (match.length == 2) {
22602                     // It's a surrogate pair, calculate the Unicode code point
22603                     var high = match.charCodeAt(0) - 0xD800;
22604                     var low  = match.charCodeAt(1) - 0xDC00;
22605                     cc = (high * 0x400) + low + 0x10000;
22606                 }  else if (
22607                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22608                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22609                     (cc >= 0xf900 && cc < 0xfb00 )
22610                 ) {
22611                         return match;
22612                 }  
22613          
22614                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22615                 return "&#" + cc + ";";
22616                 
22617                 
22618             });
22619             
22620             
22621              
22622             if(this.owner.fireEvent('beforesync', this, html) !== false){
22623                 this.el.dom.value = html;
22624                 this.owner.fireEvent('sync', this, html);
22625             }
22626         }
22627     },
22628
22629     /**
22630      * TEXTAREA -> EDITABLE
22631      * Protected method that will not generally be called directly. Pushes the value of the textarea
22632      * into the iframe editor.
22633      */
22634     pushValue : function()
22635     {
22636         Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
22637         if(this.initialized){
22638             var v = this.el.dom.value.trim();
22639             
22640             
22641             if(this.owner.fireEvent('beforepush', this, v) !== false){
22642                 var d = (this.doc.body || this.doc.documentElement);
22643                 d.innerHTML = v;
22644                  
22645                 this.el.dom.value = d.innerHTML;
22646                 this.owner.fireEvent('push', this, v);
22647             }
22648             
22649             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
22650                 
22651                 Roo.htmleditor.Block.factory(e);
22652                 
22653             },this);
22654             var lc = this.doc.body.lastChild;
22655             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
22656                 // add an extra line at the end.
22657                 this.doc.body.appendChild(this.doc.createElement('br'));
22658             }
22659             
22660             
22661         }
22662     },
22663
22664     // private
22665     deferFocus : function(){
22666         this.focus.defer(10, this);
22667     },
22668
22669     // doc'ed in Field
22670     focus : function(){
22671         if(this.win && !this.sourceEditMode){
22672             this.win.focus();
22673         }else{
22674             this.el.focus();
22675         }
22676     },
22677     
22678     assignDocWin: function()
22679     {
22680         var iframe = this.iframe;
22681         
22682          if(Roo.isIE){
22683             this.doc = iframe.contentWindow.document;
22684             this.win = iframe.contentWindow;
22685         } else {
22686 //            if (!Roo.get(this.frameId)) {
22687 //                return;
22688 //            }
22689 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22690 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22691             
22692             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22693                 return;
22694             }
22695             
22696             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22697             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22698         }
22699     },
22700     
22701     // private
22702     initEditor : function(){
22703         //console.log("INIT EDITOR");
22704         this.assignDocWin();
22705         
22706         
22707         
22708         this.doc.designMode="on";
22709         this.doc.open();
22710         this.doc.write(this.getDocMarkup());
22711         this.doc.close();
22712         
22713         var dbody = (this.doc.body || this.doc.documentElement);
22714         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22715         // this copies styles from the containing element into thsi one..
22716         // not sure why we need all of this..
22717         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22718         
22719         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22720         //ss['background-attachment'] = 'fixed'; // w3c
22721         dbody.bgProperties = 'fixed'; // ie
22722         //Roo.DomHelper.applyStyles(dbody, ss);
22723         Roo.EventManager.on(this.doc, {
22724             //'mousedown': this.onEditorEvent,
22725             'mouseup': this.onEditorEvent,
22726             'dblclick': this.onEditorEvent,
22727             'click': this.onEditorEvent,
22728             'keyup': this.onEditorEvent,
22729             
22730             buffer:100,
22731             scope: this
22732         });
22733         Roo.EventManager.on(this.doc, {
22734             'paste': this.onPasteEvent,
22735             scope : this
22736         });
22737         if(Roo.isGecko){
22738             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22739         }
22740         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22741             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22742         }
22743         this.initialized = true;
22744
22745         
22746         // initialize special key events - enter
22747         new Roo.htmleditor.KeyEnter({core : this});
22748         
22749          
22750         
22751         this.owner.fireEvent('initialize', this);
22752         this.pushValue();
22753     },
22754     
22755     onPasteEvent : function(e,v)
22756     {
22757         // I think we better assume paste is going to be a dirty load of rubish from word..
22758         
22759         // even pasting into a 'email version' of this widget will have to clean up that mess.
22760         var cd = (e.browserEvent.clipboardData || window.clipboardData);
22761         
22762         // check what type of paste - if it's an image, then handle it differently.
22763         if (cd.files.length > 0) {
22764             // pasting images?
22765             var urlAPI = (window.createObjectURL && window) || 
22766                 (window.URL && URL.revokeObjectURL && URL) || 
22767                 (window.webkitURL && webkitURL);
22768     
22769             var url = urlAPI.createObjectURL( cd.files[0]);
22770             this.insertAtCursor('<img src=" + url + ">');
22771             return false;
22772         }
22773         
22774         var html = cd.getData('text/html'); // clipboard event
22775         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
22776         var images = parser.doc.getElementsByType('pict');
22777         Roo.log(images);
22778         //Roo.log(imgs);
22779         // fixme..
22780         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
22781                        .map(function(g) { return g.toDataURL(); });
22782         
22783         
22784         html = this.cleanWordChars(html);
22785         
22786         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
22787         
22788         if (images.length > 0) {
22789             Roo.each(d.getElementsByTagName('img'), function(img, i) {
22790                 img.setAttribute('src', images[i]);
22791             });
22792         }
22793         
22794       
22795         new Roo.htmleditor.FilterStyleToTag({ node : d });
22796         new Roo.htmleditor.FilterAttributes({
22797             node : d,
22798             attrib_white : ['href', 'src', 'name', 'align'],
22799             attrib_clean : ['href', 'src' ] 
22800         });
22801         new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
22802         // should be fonts..
22803         new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
22804         new Roo.htmleditor.FilterParagraph({ node : d });
22805         new Roo.htmleditor.FilterSpan({ node : d });
22806         new Roo.htmleditor.FilterLongBr({ node : d });
22807         
22808         
22809         
22810         this.insertAtCursor(d.innerHTML);
22811         
22812         e.preventDefault();
22813         return false;
22814         // default behaveiour should be our local cleanup paste? (optional?)
22815         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
22816         //this.owner.fireEvent('paste', e, v);
22817     },
22818     // private
22819     onDestroy : function(){
22820         
22821         
22822         
22823         if(this.rendered){
22824             
22825             //for (var i =0; i < this.toolbars.length;i++) {
22826             //    // fixme - ask toolbars for heights?
22827             //    this.toolbars[i].onDestroy();
22828            // }
22829             
22830             //this.wrap.dom.innerHTML = '';
22831             //this.wrap.remove();
22832         }
22833     },
22834
22835     // private
22836     onFirstFocus : function(){
22837         
22838         this.assignDocWin();
22839         
22840         
22841         this.activated = true;
22842          
22843     
22844         if(Roo.isGecko){ // prevent silly gecko errors
22845             this.win.focus();
22846             var s = this.win.getSelection();
22847             if(!s.focusNode || s.focusNode.nodeType != 3){
22848                 var r = s.getRangeAt(0);
22849                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22850                 r.collapse(true);
22851                 this.deferFocus();
22852             }
22853             try{
22854                 this.execCmd('useCSS', true);
22855                 this.execCmd('styleWithCSS', false);
22856             }catch(e){}
22857         }
22858         this.owner.fireEvent('activate', this);
22859     },
22860
22861     // private
22862     adjustFont: function(btn){
22863         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22864         //if(Roo.isSafari){ // safari
22865         //    adjust *= 2;
22866        // }
22867         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22868         if(Roo.isSafari){ // safari
22869             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22870             v =  (v < 10) ? 10 : v;
22871             v =  (v > 48) ? 48 : v;
22872             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22873             
22874         }
22875         
22876         
22877         v = Math.max(1, v+adjust);
22878         
22879         this.execCmd('FontSize', v  );
22880     },
22881
22882     onEditorEvent : function(e)
22883     {
22884         this.owner.fireEvent('editorevent', this, e);
22885       //  this.updateToolbar();
22886         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22887     },
22888
22889     insertTag : function(tg)
22890     {
22891         // could be a bit smarter... -> wrap the current selected tRoo..
22892         if (tg.toLowerCase() == 'span' ||
22893             tg.toLowerCase() == 'code' ||
22894             tg.toLowerCase() == 'sup' ||
22895             tg.toLowerCase() == 'sub' 
22896             ) {
22897             
22898             range = this.createRange(this.getSelection());
22899             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22900             wrappingNode.appendChild(range.extractContents());
22901             range.insertNode(wrappingNode);
22902
22903             return;
22904             
22905             
22906             
22907         }
22908         this.execCmd("formatblock",   tg);
22909         
22910     },
22911     
22912     insertText : function(txt)
22913     {
22914         
22915         
22916         var range = this.createRange();
22917         range.deleteContents();
22918                //alert(Sender.getAttribute('label'));
22919                
22920         range.insertNode(this.doc.createTextNode(txt));
22921     } ,
22922     
22923      
22924
22925     /**
22926      * Executes a Midas editor command on the editor document and performs necessary focus and
22927      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22928      * @param {String} cmd The Midas command
22929      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22930      */
22931     relayCmd : function(cmd, value){
22932         this.win.focus();
22933         this.execCmd(cmd, value);
22934         this.owner.fireEvent('editorevent', this);
22935         //this.updateToolbar();
22936         this.owner.deferFocus();
22937     },
22938
22939     /**
22940      * Executes a Midas editor command directly on the editor document.
22941      * For visual commands, you should use {@link #relayCmd} instead.
22942      * <b>This should only be called after the editor is initialized.</b>
22943      * @param {String} cmd The Midas command
22944      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22945      */
22946     execCmd : function(cmd, value){
22947         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22948         this.syncValue();
22949     },
22950  
22951  
22952    
22953     /**
22954      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22955      * to insert tRoo.
22956      * @param {String} text | dom node.. 
22957      */
22958     insertAtCursor : function(text)
22959     {
22960         
22961         if(!this.activated){
22962             return;
22963         }
22964          
22965         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22966             this.win.focus();
22967             
22968             
22969             // from jquery ui (MIT licenced)
22970             var range, node;
22971             var win = this.win;
22972             
22973             if (win.getSelection && win.getSelection().getRangeAt) {
22974                 
22975                 // delete the existing?
22976                 
22977                 this.createRange(this.getSelection()).deleteContents();
22978                 range = win.getSelection().getRangeAt(0);
22979                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22980                 range.insertNode(node);
22981             } else if (win.document.selection && win.document.selection.createRange) {
22982                 // no firefox support
22983                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22984                 win.document.selection.createRange().pasteHTML(txt);
22985             } else {
22986                 // no firefox support
22987                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22988                 this.execCmd('InsertHTML', txt);
22989             } 
22990             
22991             this.syncValue();
22992             
22993             this.deferFocus();
22994         }
22995     },
22996  // private
22997     mozKeyPress : function(e){
22998         if(e.ctrlKey){
22999             var c = e.getCharCode(), cmd;
23000           
23001             if(c > 0){
23002                 c = String.fromCharCode(c).toLowerCase();
23003                 switch(c){
23004                     case 'b':
23005                         cmd = 'bold';
23006                         break;
23007                     case 'i':
23008                         cmd = 'italic';
23009                         break;
23010                     
23011                     case 'u':
23012                         cmd = 'underline';
23013                         break;
23014                     
23015                     //case 'v':
23016                       //  this.cleanUpPaste.defer(100, this);
23017                       //  return;
23018                         
23019                 }
23020                 if(cmd){
23021                     this.win.focus();
23022                     this.execCmd(cmd);
23023                     this.deferFocus();
23024                     e.preventDefault();
23025                 }
23026                 
23027             }
23028         }
23029     },
23030
23031     // private
23032     fixKeys : function(){ // load time branching for fastest keydown performance
23033         if(Roo.isIE){
23034             return function(e){
23035                 var k = e.getKey(), r;
23036                 if(k == e.TAB){
23037                     e.stopEvent();
23038                     r = this.doc.selection.createRange();
23039                     if(r){
23040                         r.collapse(true);
23041                         r.pasteHTML('&#160;&#160;&#160;&#160;');
23042                         this.deferFocus();
23043                     }
23044                     return;
23045                 }
23046                 
23047                 if(k == e.ENTER){
23048                     r = this.doc.selection.createRange();
23049                     if(r){
23050                         var target = r.parentElement();
23051                         if(!target || target.tagName.toLowerCase() != 'li'){
23052                             e.stopEvent();
23053                             r.pasteHTML('<br/>');
23054                             r.collapse(false);
23055                             r.select();
23056                         }
23057                     }
23058                 }
23059                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23060                 //    this.cleanUpPaste.defer(100, this);
23061                 //    return;
23062                 //}
23063                 
23064                 
23065             };
23066         }else if(Roo.isOpera){
23067             return function(e){
23068                 var k = e.getKey();
23069                 if(k == e.TAB){
23070                     e.stopEvent();
23071                     this.win.focus();
23072                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
23073                     this.deferFocus();
23074                 }
23075                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23076                 //    this.cleanUpPaste.defer(100, this);
23077                  //   return;
23078                 //}
23079                 
23080             };
23081         }else if(Roo.isSafari){
23082             return function(e){
23083                 var k = e.getKey();
23084                 
23085                 if(k == e.TAB){
23086                     e.stopEvent();
23087                     this.execCmd('InsertText','\t');
23088                     this.deferFocus();
23089                     return;
23090                 }
23091                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23092                  //   this.cleanUpPaste.defer(100, this);
23093                  //   return;
23094                // }
23095                 
23096              };
23097         }
23098     }(),
23099     
23100     getAllAncestors: function()
23101     {
23102         var p = this.getSelectedNode();
23103         var a = [];
23104         if (!p) {
23105             a.push(p); // push blank onto stack..
23106             p = this.getParentElement();
23107         }
23108         
23109         
23110         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23111             a.push(p);
23112             p = p.parentNode;
23113         }
23114         a.push(this.doc.body);
23115         return a;
23116     },
23117     lastSel : false,
23118     lastSelNode : false,
23119     
23120     
23121     getSelection : function() 
23122     {
23123         this.assignDocWin();
23124         return Roo.isIE ? this.doc.selection : this.win.getSelection();
23125     },
23126     /**
23127      * Select a dom node
23128      * @param {DomElement} node the node to select
23129      */
23130     selectNode : function(node)
23131     {
23132         
23133             var nodeRange = node.ownerDocument.createRange();
23134             try {
23135                 nodeRange.selectNode(node);
23136             } catch (e) {
23137                 nodeRange.selectNodeContents(node);
23138             }
23139             //nodeRange.collapse(true);
23140             var s = this.win.getSelection();
23141             s.removeAllRanges();
23142             s.addRange(nodeRange);
23143     },
23144     
23145     getSelectedNode: function() 
23146     {
23147         // this may only work on Gecko!!!
23148         
23149         // should we cache this!!!!
23150         
23151         
23152         
23153          
23154         var range = this.createRange(this.getSelection()).cloneRange();
23155         
23156         if (Roo.isIE) {
23157             var parent = range.parentElement();
23158             while (true) {
23159                 var testRange = range.duplicate();
23160                 testRange.moveToElementText(parent);
23161                 if (testRange.inRange(range)) {
23162                     break;
23163                 }
23164                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23165                     break;
23166                 }
23167                 parent = parent.parentElement;
23168             }
23169             return parent;
23170         }
23171         
23172         // is ancestor a text element.
23173         var ac =  range.commonAncestorContainer;
23174         if (ac.nodeType == 3) {
23175             ac = ac.parentNode;
23176         }
23177         
23178         var ar = ac.childNodes;
23179          
23180         var nodes = [];
23181         var other_nodes = [];
23182         var has_other_nodes = false;
23183         for (var i=0;i<ar.length;i++) {
23184             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
23185                 continue;
23186             }
23187             // fullly contained node.
23188             
23189             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23190                 nodes.push(ar[i]);
23191                 continue;
23192             }
23193             
23194             // probably selected..
23195             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23196                 other_nodes.push(ar[i]);
23197                 continue;
23198             }
23199             // outer..
23200             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
23201                 continue;
23202             }
23203             
23204             
23205             has_other_nodes = true;
23206         }
23207         if (!nodes.length && other_nodes.length) {
23208             nodes= other_nodes;
23209         }
23210         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23211             return false;
23212         }
23213         
23214         return nodes[0];
23215     },
23216     createRange: function(sel)
23217     {
23218         // this has strange effects when using with 
23219         // top toolbar - not sure if it's a great idea.
23220         //this.editor.contentWindow.focus();
23221         if (typeof sel != "undefined") {
23222             try {
23223                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23224             } catch(e) {
23225                 return this.doc.createRange();
23226             }
23227         } else {
23228             return this.doc.createRange();
23229         }
23230     },
23231     getParentElement: function()
23232     {
23233         
23234         this.assignDocWin();
23235         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23236         
23237         var range = this.createRange(sel);
23238          
23239         try {
23240             var p = range.commonAncestorContainer;
23241             while (p.nodeType == 3) { // text node
23242                 p = p.parentNode;
23243             }
23244             return p;
23245         } catch (e) {
23246             return null;
23247         }
23248     
23249     },
23250     /***
23251      *
23252      * Range intersection.. the hard stuff...
23253      *  '-1' = before
23254      *  '0' = hits..
23255      *  '1' = after.
23256      *         [ -- selected range --- ]
23257      *   [fail]                        [fail]
23258      *
23259      *    basically..
23260      *      if end is before start or  hits it. fail.
23261      *      if start is after end or hits it fail.
23262      *
23263      *   if either hits (but other is outside. - then it's not 
23264      *   
23265      *    
23266      **/
23267     
23268     
23269     // @see http://www.thismuchiknow.co.uk/?p=64.
23270     rangeIntersectsNode : function(range, node)
23271     {
23272         var nodeRange = node.ownerDocument.createRange();
23273         try {
23274             nodeRange.selectNode(node);
23275         } catch (e) {
23276             nodeRange.selectNodeContents(node);
23277         }
23278     
23279         var rangeStartRange = range.cloneRange();
23280         rangeStartRange.collapse(true);
23281     
23282         var rangeEndRange = range.cloneRange();
23283         rangeEndRange.collapse(false);
23284     
23285         var nodeStartRange = nodeRange.cloneRange();
23286         nodeStartRange.collapse(true);
23287     
23288         var nodeEndRange = nodeRange.cloneRange();
23289         nodeEndRange.collapse(false);
23290     
23291         return rangeStartRange.compareBoundaryPoints(
23292                  Range.START_TO_START, nodeEndRange) == -1 &&
23293                rangeEndRange.compareBoundaryPoints(
23294                  Range.START_TO_START, nodeStartRange) == 1;
23295         
23296          
23297     },
23298     rangeCompareNode : function(range, node)
23299     {
23300         var nodeRange = node.ownerDocument.createRange();
23301         try {
23302             nodeRange.selectNode(node);
23303         } catch (e) {
23304             nodeRange.selectNodeContents(node);
23305         }
23306         
23307         
23308         range.collapse(true);
23309     
23310         nodeRange.collapse(true);
23311      
23312         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23313         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
23314          
23315         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23316         
23317         var nodeIsBefore   =  ss == 1;
23318         var nodeIsAfter    = ee == -1;
23319         
23320         if (nodeIsBefore && nodeIsAfter) {
23321             return 0; // outer
23322         }
23323         if (!nodeIsBefore && nodeIsAfter) {
23324             return 1; //right trailed.
23325         }
23326         
23327         if (nodeIsBefore && !nodeIsAfter) {
23328             return 2;  // left trailed.
23329         }
23330         // fully contined.
23331         return 3;
23332     },
23333  
23334     cleanWordChars : function(input) {// change the chars to hex code
23335         
23336        var swapCodes  = [ 
23337             [    8211, "&#8211;" ], 
23338             [    8212, "&#8212;" ], 
23339             [    8216,  "'" ],  
23340             [    8217, "'" ],  
23341             [    8220, '"' ],  
23342             [    8221, '"' ],  
23343             [    8226, "*" ],  
23344             [    8230, "..." ]
23345         ]; 
23346         var output = input;
23347         Roo.each(swapCodes, function(sw) { 
23348             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23349             
23350             output = output.replace(swapper, sw[1]);
23351         });
23352         
23353         return output;
23354     },
23355     
23356      
23357     
23358         
23359     
23360     cleanUpChild : function (node)
23361     {
23362         
23363         new Roo.htmleditor.FilterComment({node : node});
23364         new Roo.htmleditor.FilterAttributes({
23365                 node : node,
23366                 attrib_black : this.ablack,
23367                 attrib_clean : this.aclean,
23368                 style_white : this.cwhite,
23369                 style_black : this.cblack
23370         });
23371         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
23372         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
23373          
23374         
23375     },
23376     
23377     /**
23378      * Clean up MS wordisms...
23379      * @deprecated - use filter directly
23380      */
23381     cleanWord : function(node)
23382     {
23383         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
23384         
23385     },
23386    
23387     
23388     /**
23389
23390      * @deprecated - use filters
23391      */
23392     cleanTableWidths : function(node)
23393     {
23394         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
23395         
23396  
23397     },
23398     
23399      
23400         
23401     applyBlacklists : function()
23402     {
23403         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23404         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23405         
23406         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
23407         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
23408         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
23409         
23410         this.white = [];
23411         this.black = [];
23412         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23413             if (b.indexOf(tag) > -1) {
23414                 return;
23415             }
23416             this.white.push(tag);
23417             
23418         }, this);
23419         
23420         Roo.each(w, function(tag) {
23421             if (b.indexOf(tag) > -1) {
23422                 return;
23423             }
23424             if (this.white.indexOf(tag) > -1) {
23425                 return;
23426             }
23427             this.white.push(tag);
23428             
23429         }, this);
23430         
23431         
23432         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23433             if (w.indexOf(tag) > -1) {
23434                 return;
23435             }
23436             this.black.push(tag);
23437             
23438         }, this);
23439         
23440         Roo.each(b, function(tag) {
23441             if (w.indexOf(tag) > -1) {
23442                 return;
23443             }
23444             if (this.black.indexOf(tag) > -1) {
23445                 return;
23446             }
23447             this.black.push(tag);
23448             
23449         }, this);
23450         
23451         
23452         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23453         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23454         
23455         this.cwhite = [];
23456         this.cblack = [];
23457         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23458             if (b.indexOf(tag) > -1) {
23459                 return;
23460             }
23461             this.cwhite.push(tag);
23462             
23463         }, this);
23464         
23465         Roo.each(w, function(tag) {
23466             if (b.indexOf(tag) > -1) {
23467                 return;
23468             }
23469             if (this.cwhite.indexOf(tag) > -1) {
23470                 return;
23471             }
23472             this.cwhite.push(tag);
23473             
23474         }, this);
23475         
23476         
23477         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23478             if (w.indexOf(tag) > -1) {
23479                 return;
23480             }
23481             this.cblack.push(tag);
23482             
23483         }, this);
23484         
23485         Roo.each(b, function(tag) {
23486             if (w.indexOf(tag) > -1) {
23487                 return;
23488             }
23489             if (this.cblack.indexOf(tag) > -1) {
23490                 return;
23491             }
23492             this.cblack.push(tag);
23493             
23494         }, this);
23495     },
23496     
23497     setStylesheets : function(stylesheets)
23498     {
23499         if(typeof(stylesheets) == 'string'){
23500             Roo.get(this.iframe.contentDocument.head).createChild({
23501                 tag : 'link',
23502                 rel : 'stylesheet',
23503                 type : 'text/css',
23504                 href : stylesheets
23505             });
23506             
23507             return;
23508         }
23509         var _this = this;
23510      
23511         Roo.each(stylesheets, function(s) {
23512             if(!s.length){
23513                 return;
23514             }
23515             
23516             Roo.get(_this.iframe.contentDocument.head).createChild({
23517                 tag : 'link',
23518                 rel : 'stylesheet',
23519                 type : 'text/css',
23520                 href : s
23521             });
23522         });
23523
23524         
23525     },
23526     
23527     removeStylesheets : function()
23528     {
23529         var _this = this;
23530         
23531         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23532             s.remove();
23533         });
23534     },
23535     
23536     setStyle : function(style)
23537     {
23538         Roo.get(this.iframe.contentDocument.head).createChild({
23539             tag : 'style',
23540             type : 'text/css',
23541             html : style
23542         });
23543
23544         return;
23545     }
23546     
23547     // hide stuff that is not compatible
23548     /**
23549      * @event blur
23550      * @hide
23551      */
23552     /**
23553      * @event change
23554      * @hide
23555      */
23556     /**
23557      * @event focus
23558      * @hide
23559      */
23560     /**
23561      * @event specialkey
23562      * @hide
23563      */
23564     /**
23565      * @cfg {String} fieldClass @hide
23566      */
23567     /**
23568      * @cfg {String} focusClass @hide
23569      */
23570     /**
23571      * @cfg {String} autoCreate @hide
23572      */
23573     /**
23574      * @cfg {String} inputType @hide
23575      */
23576     /**
23577      * @cfg {String} invalidClass @hide
23578      */
23579     /**
23580      * @cfg {String} invalidText @hide
23581      */
23582     /**
23583      * @cfg {String} msgFx @hide
23584      */
23585     /**
23586      * @cfg {String} validateOnBlur @hide
23587      */
23588 });
23589
23590 Roo.HtmlEditorCore.white = [
23591         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
23592         
23593        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
23594        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
23595        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
23596        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
23597        'TABLE',   'UL',         'XMP', 
23598        
23599        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
23600       'THEAD',   'TR', 
23601      
23602       'DIR', 'MENU', 'OL', 'UL', 'DL',
23603        
23604       'EMBED',  'OBJECT'
23605 ];
23606
23607
23608 Roo.HtmlEditorCore.black = [
23609     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23610         'APPLET', // 
23611         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
23612         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
23613         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
23614         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
23615         //'FONT' // CLEAN LATER..
23616         'COLGROUP', 'COL'  // messy tables.
23617         
23618 ];
23619 Roo.HtmlEditorCore.clean = [ // ?? needed???
23620      'SCRIPT', 'STYLE', 'TITLE', 'XML'
23621 ];
23622 Roo.HtmlEditorCore.tag_remove = [
23623     'FONT', 'TBODY'  
23624 ];
23625 // attributes..
23626
23627 Roo.HtmlEditorCore.ablack = [
23628     'on'
23629 ];
23630     
23631 Roo.HtmlEditorCore.aclean = [ 
23632     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23633 ];
23634
23635 // protocols..
23636 Roo.HtmlEditorCore.pwhite= [
23637         'http',  'https',  'mailto'
23638 ];
23639
23640 // white listed style attributes.
23641 Roo.HtmlEditorCore.cwhite= [
23642       //  'text-align', /// default is to allow most things..
23643       
23644          
23645 //        'font-size'//??
23646 ];
23647
23648 // black listed style attributes.
23649 Roo.HtmlEditorCore.cblack= [
23650       //  'font-size' -- this can be set by the project 
23651 ];
23652
23653
23654
23655
23656     //<script type="text/javascript">
23657
23658 /*
23659  * Ext JS Library 1.1.1
23660  * Copyright(c) 2006-2007, Ext JS, LLC.
23661  * Licence LGPL
23662  * 
23663  */
23664  
23665  
23666 Roo.form.HtmlEditor = function(config){
23667     
23668     
23669     
23670     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
23671     
23672     if (!this.toolbars) {
23673         this.toolbars = [];
23674     }
23675     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23676     
23677     
23678 };
23679
23680 /**
23681  * @class Roo.form.HtmlEditor
23682  * @extends Roo.form.Field
23683  * Provides a lightweight HTML Editor component.
23684  *
23685  * This has been tested on Fireforx / Chrome.. IE may not be so great..
23686  * 
23687  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23688  * supported by this editor.</b><br/><br/>
23689  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23690  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23691  */
23692 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
23693     /**
23694      * @cfg {Boolean} clearUp
23695      */
23696     clearUp : true,
23697       /**
23698      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23699      */
23700     toolbars : false,
23701    
23702      /**
23703      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23704      *                        Roo.resizable.
23705      */
23706     resizable : false,
23707      /**
23708      * @cfg {Number} height (in pixels)
23709      */   
23710     height: 300,
23711    /**
23712      * @cfg {Number} width (in pixels)
23713      */   
23714     width: 500,
23715     
23716     /**
23717      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
23718      * 
23719      */
23720     stylesheets: false,
23721     
23722     
23723      /**
23724      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
23725      * 
23726      */
23727     cblack: false,
23728     /**
23729      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
23730      * 
23731      */
23732     cwhite: false,
23733     
23734      /**
23735      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
23736      * 
23737      */
23738     black: false,
23739     /**
23740      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
23741      * 
23742      */
23743     white: false,
23744     /**
23745      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
23746      */
23747     allowComments: false,
23748     /**
23749      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
23750      */
23751     
23752     
23753      bodyCls : '',
23754     
23755     // id of frame..
23756     frameId: false,
23757     
23758     // private properties
23759     validationEvent : false,
23760     deferHeight: true,
23761     initialized : false,
23762     activated : false,
23763     
23764     onFocus : Roo.emptyFn,
23765     iframePad:3,
23766     hideMode:'offsets',
23767     
23768     actionMode : 'container', // defaults to hiding it...
23769     
23770     defaultAutoCreate : { // modified by initCompnoent..
23771         tag: "textarea",
23772         style:"width:500px;height:300px;",
23773         autocomplete: "new-password"
23774     },
23775
23776     // private
23777     initComponent : function(){
23778         this.addEvents({
23779             /**
23780              * @event initialize
23781              * Fires when the editor is fully initialized (including the iframe)
23782              * @param {HtmlEditor} this
23783              */
23784             initialize: true,
23785             /**
23786              * @event activate
23787              * Fires when the editor is first receives the focus. Any insertion must wait
23788              * until after this event.
23789              * @param {HtmlEditor} this
23790              */
23791             activate: true,
23792              /**
23793              * @event beforesync
23794              * Fires before the textarea is updated with content from the editor iframe. Return false
23795              * to cancel the sync.
23796              * @param {HtmlEditor} this
23797              * @param {String} html
23798              */
23799             beforesync: true,
23800              /**
23801              * @event beforepush
23802              * Fires before the iframe editor is updated with content from the textarea. Return false
23803              * to cancel the push.
23804              * @param {HtmlEditor} this
23805              * @param {String} html
23806              */
23807             beforepush: true,
23808              /**
23809              * @event sync
23810              * Fires when the textarea is updated with content from the editor iframe.
23811              * @param {HtmlEditor} this
23812              * @param {String} html
23813              */
23814             sync: true,
23815              /**
23816              * @event push
23817              * Fires when the iframe editor is updated with content from the textarea.
23818              * @param {HtmlEditor} this
23819              * @param {String} html
23820              */
23821             push: true,
23822              /**
23823              * @event editmodechange
23824              * Fires when the editor switches edit modes
23825              * @param {HtmlEditor} this
23826              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23827              */
23828             editmodechange: true,
23829             /**
23830              * @event editorevent
23831              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23832              * @param {HtmlEditor} this
23833              */
23834             editorevent: true,
23835             /**
23836              * @event firstfocus
23837              * Fires when on first focus - needed by toolbars..
23838              * @param {HtmlEditor} this
23839              */
23840             firstfocus: true,
23841             /**
23842              * @event autosave
23843              * Auto save the htmlEditor value as a file into Events
23844              * @param {HtmlEditor} this
23845              */
23846             autosave: true,
23847             /**
23848              * @event savedpreview
23849              * preview the saved version of htmlEditor
23850              * @param {HtmlEditor} this
23851              */
23852             savedpreview: true,
23853             
23854             /**
23855             * @event stylesheetsclick
23856             * Fires when press the Sytlesheets button
23857             * @param {Roo.HtmlEditorCore} this
23858             */
23859             stylesheetsclick: true,
23860             /**
23861             * @event paste
23862             * Fires when press user pastes into the editor
23863             * @param {Roo.HtmlEditorCore} this
23864             */
23865             paste: true 
23866         });
23867         this.defaultAutoCreate =  {
23868             tag: "textarea",
23869             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
23870             autocomplete: "new-password"
23871         };
23872     },
23873
23874     /**
23875      * Protected method that will not generally be called directly. It
23876      * is called when the editor creates its toolbar. Override this method if you need to
23877      * add custom toolbar buttons.
23878      * @param {HtmlEditor} editor
23879      */
23880     createToolbar : function(editor){
23881         Roo.log("create toolbars");
23882         if (!editor.toolbars || !editor.toolbars.length) {
23883             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
23884         }
23885         
23886         for (var i =0 ; i < editor.toolbars.length;i++) {
23887             editor.toolbars[i] = Roo.factory(
23888                     typeof(editor.toolbars[i]) == 'string' ?
23889                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
23890                 Roo.form.HtmlEditor);
23891             editor.toolbars[i].init(editor);
23892         }
23893          
23894         
23895     },
23896
23897      
23898     // private
23899     onRender : function(ct, position)
23900     {
23901         var _t = this;
23902         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
23903         
23904         this.wrap = this.el.wrap({
23905             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23906         });
23907         
23908         this.editorcore.onRender(ct, position);
23909          
23910         if (this.resizable) {
23911             this.resizeEl = new Roo.Resizable(this.wrap, {
23912                 pinned : true,
23913                 wrap: true,
23914                 dynamic : true,
23915                 minHeight : this.height,
23916                 height: this.height,
23917                 handles : this.resizable,
23918                 width: this.width,
23919                 listeners : {
23920                     resize : function(r, w, h) {
23921                         _t.onResize(w,h); // -something
23922                     }
23923                 }
23924             });
23925             
23926         }
23927         this.createToolbar(this);
23928        
23929         
23930         if(!this.width){
23931             this.setSize(this.wrap.getSize());
23932         }
23933         if (this.resizeEl) {
23934             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23935             // should trigger onReize..
23936         }
23937         
23938         this.keyNav = new Roo.KeyNav(this.el, {
23939             
23940             "tab" : function(e){
23941                 e.preventDefault();
23942                 
23943                 var value = this.getValue();
23944                 
23945                 var start = this.el.dom.selectionStart;
23946                 var end = this.el.dom.selectionEnd;
23947                 
23948                 if(!e.shiftKey){
23949                     
23950                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
23951                     this.el.dom.setSelectionRange(end + 1, end + 1);
23952                     return;
23953                 }
23954                 
23955                 var f = value.substring(0, start).split("\t");
23956                 
23957                 if(f.pop().length != 0){
23958                     return;
23959                 }
23960                 
23961                 this.setValue(f.join("\t") + value.substring(end));
23962                 this.el.dom.setSelectionRange(start - 1, start - 1);
23963                 
23964             },
23965             
23966             "home" : function(e){
23967                 e.preventDefault();
23968                 
23969                 var curr = this.el.dom.selectionStart;
23970                 var lines = this.getValue().split("\n");
23971                 
23972                 if(!lines.length){
23973                     return;
23974                 }
23975                 
23976                 if(e.ctrlKey){
23977                     this.el.dom.setSelectionRange(0, 0);
23978                     return;
23979                 }
23980                 
23981                 var pos = 0;
23982                 
23983                 for (var i = 0; i < lines.length;i++) {
23984                     pos += lines[i].length;
23985                     
23986                     if(i != 0){
23987                         pos += 1;
23988                     }
23989                     
23990                     if(pos < curr){
23991                         continue;
23992                     }
23993                     
23994                     pos -= lines[i].length;
23995                     
23996                     break;
23997                 }
23998                 
23999                 if(!e.shiftKey){
24000                     this.el.dom.setSelectionRange(pos, pos);
24001                     return;
24002                 }
24003                 
24004                 this.el.dom.selectionStart = pos;
24005                 this.el.dom.selectionEnd = curr;
24006             },
24007             
24008             "end" : function(e){
24009                 e.preventDefault();
24010                 
24011                 var curr = this.el.dom.selectionStart;
24012                 var lines = this.getValue().split("\n");
24013                 
24014                 if(!lines.length){
24015                     return;
24016                 }
24017                 
24018                 if(e.ctrlKey){
24019                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
24020                     return;
24021                 }
24022                 
24023                 var pos = 0;
24024                 
24025                 for (var i = 0; i < lines.length;i++) {
24026                     
24027                     pos += lines[i].length;
24028                     
24029                     if(i != 0){
24030                         pos += 1;
24031                     }
24032                     
24033                     if(pos < curr){
24034                         continue;
24035                     }
24036                     
24037                     break;
24038                 }
24039                 
24040                 if(!e.shiftKey){
24041                     this.el.dom.setSelectionRange(pos, pos);
24042                     return;
24043                 }
24044                 
24045                 this.el.dom.selectionStart = curr;
24046                 this.el.dom.selectionEnd = pos;
24047             },
24048
24049             scope : this,
24050
24051             doRelay : function(foo, bar, hname){
24052                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
24053             },
24054
24055             forceKeyDown: true
24056         });
24057         
24058 //        if(this.autosave && this.w){
24059 //            this.autoSaveFn = setInterval(this.autosave, 1000);
24060 //        }
24061     },
24062
24063     // private
24064     onResize : function(w, h)
24065     {
24066         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24067         var ew = false;
24068         var eh = false;
24069         
24070         if(this.el ){
24071             if(typeof w == 'number'){
24072                 var aw = w - this.wrap.getFrameWidth('lr');
24073                 this.el.setWidth(this.adjustWidth('textarea', aw));
24074                 ew = aw;
24075             }
24076             if(typeof h == 'number'){
24077                 var tbh = 0;
24078                 for (var i =0; i < this.toolbars.length;i++) {
24079                     // fixme - ask toolbars for heights?
24080                     tbh += this.toolbars[i].tb.el.getHeight();
24081                     if (this.toolbars[i].footer) {
24082                         tbh += this.toolbars[i].footer.el.getHeight();
24083                     }
24084                 }
24085                 
24086                 
24087                 
24088                 
24089                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24090                 ah -= 5; // knock a few pixes off for look..
24091 //                Roo.log(ah);
24092                 this.el.setHeight(this.adjustWidth('textarea', ah));
24093                 var eh = ah;
24094             }
24095         }
24096         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24097         this.editorcore.onResize(ew,eh);
24098         
24099     },
24100
24101     /**
24102      * Toggles the editor between standard and source edit mode.
24103      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24104      */
24105     toggleSourceEdit : function(sourceEditMode)
24106     {
24107         this.editorcore.toggleSourceEdit(sourceEditMode);
24108         
24109         if(this.editorcore.sourceEditMode){
24110             Roo.log('editor - showing textarea');
24111             
24112 //            Roo.log('in');
24113 //            Roo.log(this.syncValue());
24114             this.editorcore.syncValue();
24115             this.el.removeClass('x-hidden');
24116             this.el.dom.removeAttribute('tabIndex');
24117             this.el.focus();
24118             this.el.dom.scrollTop = 0;
24119             
24120             
24121             for (var i = 0; i < this.toolbars.length; i++) {
24122                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
24123                     this.toolbars[i].tb.hide();
24124                     this.toolbars[i].footer.hide();
24125                 }
24126             }
24127             
24128         }else{
24129             Roo.log('editor - hiding textarea');
24130 //            Roo.log('out')
24131 //            Roo.log(this.pushValue()); 
24132             this.editorcore.pushValue();
24133             
24134             this.el.addClass('x-hidden');
24135             this.el.dom.setAttribute('tabIndex', -1);
24136             
24137             for (var i = 0; i < this.toolbars.length; i++) {
24138                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
24139                     this.toolbars[i].tb.show();
24140                     this.toolbars[i].footer.show();
24141                 }
24142             }
24143             
24144             //this.deferFocus();
24145         }
24146         
24147         this.setSize(this.wrap.getSize());
24148         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
24149         
24150         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24151     },
24152  
24153     // private (for BoxComponent)
24154     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24155
24156     // private (for BoxComponent)
24157     getResizeEl : function(){
24158         return this.wrap;
24159     },
24160
24161     // private (for BoxComponent)
24162     getPositionEl : function(){
24163         return this.wrap;
24164     },
24165
24166     // private
24167     initEvents : function(){
24168         this.originalValue = this.getValue();
24169     },
24170
24171     /**
24172      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24173      * @method
24174      */
24175     markInvalid : Roo.emptyFn,
24176     /**
24177      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24178      * @method
24179      */
24180     clearInvalid : Roo.emptyFn,
24181
24182     setValue : function(v){
24183         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24184         this.editorcore.pushValue();
24185     },
24186
24187      
24188     // private
24189     deferFocus : function(){
24190         this.focus.defer(10, this);
24191     },
24192
24193     // doc'ed in Field
24194     focus : function(){
24195         this.editorcore.focus();
24196         
24197     },
24198       
24199
24200     // private
24201     onDestroy : function(){
24202         
24203         
24204         
24205         if(this.rendered){
24206             
24207             for (var i =0; i < this.toolbars.length;i++) {
24208                 // fixme - ask toolbars for heights?
24209                 this.toolbars[i].onDestroy();
24210             }
24211             
24212             this.wrap.dom.innerHTML = '';
24213             this.wrap.remove();
24214         }
24215     },
24216
24217     // private
24218     onFirstFocus : function(){
24219         //Roo.log("onFirstFocus");
24220         this.editorcore.onFirstFocus();
24221          for (var i =0; i < this.toolbars.length;i++) {
24222             this.toolbars[i].onFirstFocus();
24223         }
24224         
24225     },
24226     
24227     // private
24228     syncValue : function()
24229     {
24230         this.editorcore.syncValue();
24231     },
24232     
24233     pushValue : function()
24234     {
24235         this.editorcore.pushValue();
24236     },
24237     
24238     setStylesheets : function(stylesheets)
24239     {
24240         this.editorcore.setStylesheets(stylesheets);
24241     },
24242     
24243     removeStylesheets : function()
24244     {
24245         this.editorcore.removeStylesheets();
24246     }
24247      
24248     
24249     // hide stuff that is not compatible
24250     /**
24251      * @event blur
24252      * @hide
24253      */
24254     /**
24255      * @event change
24256      * @hide
24257      */
24258     /**
24259      * @event focus
24260      * @hide
24261      */
24262     /**
24263      * @event specialkey
24264      * @hide
24265      */
24266     /**
24267      * @cfg {String} fieldClass @hide
24268      */
24269     /**
24270      * @cfg {String} focusClass @hide
24271      */
24272     /**
24273      * @cfg {String} autoCreate @hide
24274      */
24275     /**
24276      * @cfg {String} inputType @hide
24277      */
24278     /**
24279      * @cfg {String} invalidClass @hide
24280      */
24281     /**
24282      * @cfg {String} invalidText @hide
24283      */
24284     /**
24285      * @cfg {String} msgFx @hide
24286      */
24287     /**
24288      * @cfg {String} validateOnBlur @hide
24289      */
24290 });
24291  
24292     // <script type="text/javascript">
24293 /*
24294  * Based on
24295  * Ext JS Library 1.1.1
24296  * Copyright(c) 2006-2007, Ext JS, LLC.
24297  *  
24298  
24299  */
24300
24301 /**
24302  * @class Roo.form.HtmlEditorToolbar1
24303  * Basic Toolbar
24304  * 
24305  * Usage:
24306  *
24307  new Roo.form.HtmlEditor({
24308     ....
24309     toolbars : [
24310         new Roo.form.HtmlEditorToolbar1({
24311             disable : { fonts: 1 , format: 1, ..., ... , ...],
24312             btns : [ .... ]
24313         })
24314     }
24315      
24316  * 
24317  * @cfg {Object} disable List of elements to disable..
24318  * @cfg {Array} btns List of additional buttons.
24319  * 
24320  * 
24321  * NEEDS Extra CSS? 
24322  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24323  */
24324  
24325 Roo.form.HtmlEditor.ToolbarStandard = function(config)
24326 {
24327     
24328     Roo.apply(this, config);
24329     
24330     // default disabled, based on 'good practice'..
24331     this.disable = this.disable || {};
24332     Roo.applyIf(this.disable, {
24333         fontSize : true,
24334         colors : true,
24335         specialElements : true
24336     });
24337     
24338     
24339     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24340     // dont call parent... till later.
24341 }
24342
24343 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
24344     
24345     tb: false,
24346     
24347     rendered: false,
24348     
24349     editor : false,
24350     editorcore : false,
24351     /**
24352      * @cfg {Object} disable  List of toolbar elements to disable
24353          
24354      */
24355     disable : false,
24356     
24357     
24358      /**
24359      * @cfg {String} createLinkText The default text for the create link prompt
24360      */
24361     createLinkText : 'Please enter the URL for the link:',
24362     /**
24363      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
24364      */
24365     defaultLinkValue : 'http:/'+'/',
24366    
24367     
24368       /**
24369      * @cfg {Array} fontFamilies An array of available font families
24370      */
24371     fontFamilies : [
24372         'Arial',
24373         'Courier New',
24374         'Tahoma',
24375         'Times New Roman',
24376         'Verdana'
24377     ],
24378     
24379     specialChars : [
24380            "&#169;",
24381           "&#174;",     
24382           "&#8482;",    
24383           "&#163;" ,    
24384          // "&#8212;",    
24385           "&#8230;",    
24386           "&#247;" ,    
24387         //  "&#225;" ,     ?? a acute?
24388            "&#8364;"    , //Euro
24389        //   "&#8220;"    ,
24390         //  "&#8221;"    ,
24391         //  "&#8226;"    ,
24392           "&#176;"  //   , // degrees
24393
24394          // "&#233;"     , // e ecute
24395          // "&#250;"     , // u ecute?
24396     ],
24397     
24398     specialElements : [
24399         {
24400             text: "Insert Table",
24401             xtype: 'MenuItem',
24402             xns : Roo.Menu,
24403             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
24404                 
24405         },
24406         {    
24407             text: "Insert Image",
24408             xtype: 'MenuItem',
24409             xns : Roo.Menu,
24410             ihtml : '<img src="about:blank"/>'
24411             
24412         }
24413         
24414          
24415     ],
24416     
24417     
24418     inputElements : [ 
24419             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
24420             "input:submit", "input:button", "select", "textarea", "label" ],
24421     formats : [
24422         ["p"] ,  
24423         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
24424         ["pre"],[ "code"], 
24425         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
24426         ['div'],['span'],
24427         ['sup'],['sub']
24428     ],
24429     
24430     cleanStyles : [
24431         "font-size"
24432     ],
24433      /**
24434      * @cfg {String} defaultFont default font to use.
24435      */
24436     defaultFont: 'tahoma',
24437    
24438     fontSelect : false,
24439     
24440     
24441     formatCombo : false,
24442     
24443     init : function(editor)
24444     {
24445         this.editor = editor;
24446         this.editorcore = editor.editorcore ? editor.editorcore : editor;
24447         var editorcore = this.editorcore;
24448         
24449         var _t = this;
24450         
24451         var fid = editorcore.frameId;
24452         var etb = this;
24453         function btn(id, toggle, handler){
24454             var xid = fid + '-'+ id ;
24455             return {
24456                 id : xid,
24457                 cmd : id,
24458                 cls : 'x-btn-icon x-edit-'+id,
24459                 enableToggle:toggle !== false,
24460                 scope: _t, // was editor...
24461                 handler:handler||_t.relayBtnCmd,
24462                 clickEvent:'mousedown',
24463                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
24464                 tabIndex:-1
24465             };
24466         }
24467         
24468         
24469         
24470         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
24471         this.tb = tb;
24472          // stop form submits
24473         tb.el.on('click', function(e){
24474             e.preventDefault(); // what does this do?
24475         });
24476
24477         if(!this.disable.font) { // && !Roo.isSafari){
24478             /* why no safari for fonts 
24479             editor.fontSelect = tb.el.createChild({
24480                 tag:'select',
24481                 tabIndex: -1,
24482                 cls:'x-font-select',
24483                 html: this.createFontOptions()
24484             });
24485             
24486             editor.fontSelect.on('change', function(){
24487                 var font = editor.fontSelect.dom.value;
24488                 editor.relayCmd('fontname', font);
24489                 editor.deferFocus();
24490             }, editor);
24491             
24492             tb.add(
24493                 editor.fontSelect.dom,
24494                 '-'
24495             );
24496             */
24497             
24498         };
24499         if(!this.disable.formats){
24500             this.formatCombo = new Roo.form.ComboBox({
24501                 store: new Roo.data.SimpleStore({
24502                     id : 'tag',
24503                     fields: ['tag'],
24504                     data : this.formats // from states.js
24505                 }),
24506                 blockFocus : true,
24507                 name : '',
24508                 //autoCreate : {tag: "div",  size: "20"},
24509                 displayField:'tag',
24510                 typeAhead: false,
24511                 mode: 'local',
24512                 editable : false,
24513                 triggerAction: 'all',
24514                 emptyText:'Add tag',
24515                 selectOnFocus:true,
24516                 width:135,
24517                 listeners : {
24518                     'select': function(c, r, i) {
24519                         editorcore.insertTag(r.get('tag'));
24520                         editor.focus();
24521                     }
24522                 }
24523
24524             });
24525             tb.addField(this.formatCombo);
24526             
24527         }
24528         
24529         if(!this.disable.format){
24530             tb.add(
24531                 btn('bold'),
24532                 btn('italic'),
24533                 btn('underline'),
24534                 btn('strikethrough')
24535             );
24536         };
24537         if(!this.disable.fontSize){
24538             tb.add(
24539                 '-',
24540                 
24541                 
24542                 btn('increasefontsize', false, editorcore.adjustFont),
24543                 btn('decreasefontsize', false, editorcore.adjustFont)
24544             );
24545         };
24546         
24547         
24548         if(!this.disable.colors){
24549             tb.add(
24550                 '-', {
24551                     id:editorcore.frameId +'-forecolor',
24552                     cls:'x-btn-icon x-edit-forecolor',
24553                     clickEvent:'mousedown',
24554                     tooltip: this.buttonTips['forecolor'] || undefined,
24555                     tabIndex:-1,
24556                     menu : new Roo.menu.ColorMenu({
24557                         allowReselect: true,
24558                         focus: Roo.emptyFn,
24559                         value:'000000',
24560                         plain:true,
24561                         selectHandler: function(cp, color){
24562                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
24563                             editor.deferFocus();
24564                         },
24565                         scope: editorcore,
24566                         clickEvent:'mousedown'
24567                     })
24568                 }, {
24569                     id:editorcore.frameId +'backcolor',
24570                     cls:'x-btn-icon x-edit-backcolor',
24571                     clickEvent:'mousedown',
24572                     tooltip: this.buttonTips['backcolor'] || undefined,
24573                     tabIndex:-1,
24574                     menu : new Roo.menu.ColorMenu({
24575                         focus: Roo.emptyFn,
24576                         value:'FFFFFF',
24577                         plain:true,
24578                         allowReselect: true,
24579                         selectHandler: function(cp, color){
24580                             if(Roo.isGecko){
24581                                 editorcore.execCmd('useCSS', false);
24582                                 editorcore.execCmd('hilitecolor', color);
24583                                 editorcore.execCmd('useCSS', true);
24584                                 editor.deferFocus();
24585                             }else{
24586                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
24587                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
24588                                 editor.deferFocus();
24589                             }
24590                         },
24591                         scope:editorcore,
24592                         clickEvent:'mousedown'
24593                     })
24594                 }
24595             );
24596         };
24597         // now add all the items...
24598         
24599
24600         if(!this.disable.alignments){
24601             tb.add(
24602                 '-',
24603                 btn('justifyleft'),
24604                 btn('justifycenter'),
24605                 btn('justifyright')
24606             );
24607         };
24608
24609         //if(!Roo.isSafari){
24610             if(!this.disable.links){
24611                 tb.add(
24612                     '-',
24613                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
24614                 );
24615             };
24616
24617             if(!this.disable.lists){
24618                 tb.add(
24619                     '-',
24620                     btn('insertorderedlist'),
24621                     btn('insertunorderedlist')
24622                 );
24623             }
24624             if(!this.disable.sourceEdit){
24625                 tb.add(
24626                     '-',
24627                     btn('sourceedit', true, function(btn){
24628                         this.toggleSourceEdit(btn.pressed);
24629                     })
24630                 );
24631             }
24632         //}
24633         
24634         var smenu = { };
24635         // special menu.. - needs to be tidied up..
24636         if (!this.disable.special) {
24637             smenu = {
24638                 text: "&#169;",
24639                 cls: 'x-edit-none',
24640                 
24641                 menu : {
24642                     items : []
24643                 }
24644             };
24645             for (var i =0; i < this.specialChars.length; i++) {
24646                 smenu.menu.items.push({
24647                     
24648                     html: this.specialChars[i],
24649                     handler: function(a,b) {
24650                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
24651                         //editor.insertAtCursor(a.html);
24652                         
24653                     },
24654                     tabIndex:-1
24655                 });
24656             }
24657             
24658             
24659             tb.add(smenu);
24660             
24661             
24662         }
24663         
24664         var cmenu = { };
24665         if (!this.disable.cleanStyles) {
24666             cmenu = {
24667                 cls: 'x-btn-icon x-btn-clear',
24668                 
24669                 menu : {
24670                     items : []
24671                 }
24672             };
24673             for (var i =0; i < this.cleanStyles.length; i++) {
24674                 cmenu.menu.items.push({
24675                     actiontype : this.cleanStyles[i],
24676                     html: 'Remove ' + this.cleanStyles[i],
24677                     handler: function(a,b) {
24678 //                        Roo.log(a);
24679 //                        Roo.log(b);
24680                         var c = Roo.get(editorcore.doc.body);
24681                         c.select('[style]').each(function(s) {
24682                             s.dom.style.removeProperty(a.actiontype);
24683                         });
24684                         editorcore.syncValue();
24685                     },
24686                     tabIndex:-1
24687                 });
24688             }
24689             cmenu.menu.items.push({
24690                 actiontype : 'tablewidths',
24691                 html: 'Remove Table Widths',
24692                 handler: function(a,b) {
24693                     editorcore.cleanTableWidths();
24694                     editorcore.syncValue();
24695                 },
24696                 tabIndex:-1
24697             });
24698             cmenu.menu.items.push({
24699                 actiontype : 'word',
24700                 html: 'Remove MS Word Formating',
24701                 handler: function(a,b) {
24702                     editorcore.cleanWord();
24703                     editorcore.syncValue();
24704                 },
24705                 tabIndex:-1
24706             });
24707             
24708             cmenu.menu.items.push({
24709                 actiontype : 'all',
24710                 html: 'Remove All Styles',
24711                 handler: function(a,b) {
24712                     
24713                     var c = Roo.get(editorcore.doc.body);
24714                     c.select('[style]').each(function(s) {
24715                         s.dom.removeAttribute('style');
24716                     });
24717                     editorcore.syncValue();
24718                 },
24719                 tabIndex:-1
24720             });
24721             
24722             cmenu.menu.items.push({
24723                 actiontype : 'all',
24724                 html: 'Remove All CSS Classes',
24725                 handler: function(a,b) {
24726                     
24727                     var c = Roo.get(editorcore.doc.body);
24728                     c.select('[class]').each(function(s) {
24729                         s.dom.removeAttribute('class');
24730                     });
24731                     editorcore.cleanWord();
24732                     editorcore.syncValue();
24733                 },
24734                 tabIndex:-1
24735             });
24736             
24737              cmenu.menu.items.push({
24738                 actiontype : 'tidy',
24739                 html: 'Tidy HTML Source',
24740                 handler: function(a,b) {
24741                     new Roo.htmleditor.Tidy(editorcore.doc.body);
24742                     editorcore.syncValue();
24743                 },
24744                 tabIndex:-1
24745             });
24746             
24747             
24748             tb.add(cmenu);
24749         }
24750          
24751         if (!this.disable.specialElements) {
24752             var semenu = {
24753                 text: "Other;",
24754                 cls: 'x-edit-none',
24755                 menu : {
24756                     items : []
24757                 }
24758             };
24759             for (var i =0; i < this.specialElements.length; i++) {
24760                 semenu.menu.items.push(
24761                     Roo.apply({ 
24762                         handler: function(a,b) {
24763                             editor.insertAtCursor(this.ihtml);
24764                         }
24765                     }, this.specialElements[i])
24766                 );
24767                     
24768             }
24769             
24770             tb.add(semenu);
24771             
24772             
24773         }
24774          
24775         
24776         if (this.btns) {
24777             for(var i =0; i< this.btns.length;i++) {
24778                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
24779                 b.cls =  'x-edit-none';
24780                 
24781                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
24782                     b.cls += ' x-init-enable';
24783                 }
24784                 
24785                 b.scope = editorcore;
24786                 tb.add(b);
24787             }
24788         
24789         }
24790         
24791         
24792         
24793         // disable everything...
24794         
24795         this.tb.items.each(function(item){
24796             
24797            if(
24798                 item.id != editorcore.frameId+ '-sourceedit' && 
24799                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
24800             ){
24801                 
24802                 item.disable();
24803             }
24804         });
24805         this.rendered = true;
24806         
24807         // the all the btns;
24808         editor.on('editorevent', this.updateToolbar, this);
24809         // other toolbars need to implement this..
24810         //editor.on('editmodechange', this.updateToolbar, this);
24811     },
24812     
24813     
24814     relayBtnCmd : function(btn) {
24815         this.editorcore.relayCmd(btn.cmd);
24816     },
24817     // private used internally
24818     createLink : function(){
24819         Roo.log("create link?");
24820         var url = prompt(this.createLinkText, this.defaultLinkValue);
24821         if(url && url != 'http:/'+'/'){
24822             this.editorcore.relayCmd('createlink', url);
24823         }
24824     },
24825
24826     
24827     /**
24828      * Protected method that will not generally be called directly. It triggers
24829      * a toolbar update by reading the markup state of the current selection in the editor.
24830      */
24831     updateToolbar: function(){
24832
24833         if(!this.editorcore.activated){
24834             this.editor.onFirstFocus();
24835             return;
24836         }
24837
24838         var btns = this.tb.items.map, 
24839             doc = this.editorcore.doc,
24840             frameId = this.editorcore.frameId;
24841
24842         if(!this.disable.font && !Roo.isSafari){
24843             /*
24844             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
24845             if(name != this.fontSelect.dom.value){
24846                 this.fontSelect.dom.value = name;
24847             }
24848             */
24849         }
24850         if(!this.disable.format){
24851             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
24852             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
24853             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
24854             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
24855         }
24856         if(!this.disable.alignments){
24857             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
24858             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
24859             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
24860         }
24861         if(!Roo.isSafari && !this.disable.lists){
24862             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
24863             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
24864         }
24865         
24866         var ans = this.editorcore.getAllAncestors();
24867         if (this.formatCombo) {
24868             
24869             
24870             var store = this.formatCombo.store;
24871             this.formatCombo.setValue("");
24872             for (var i =0; i < ans.length;i++) {
24873                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24874                     // select it..
24875                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24876                     break;
24877                 }
24878             }
24879         }
24880         
24881         
24882         
24883         // hides menus... - so this cant be on a menu...
24884         Roo.menu.MenuMgr.hideAll();
24885
24886         //this.editorsyncValue();
24887     },
24888    
24889     
24890     createFontOptions : function(){
24891         var buf = [], fs = this.fontFamilies, ff, lc;
24892         
24893         
24894         
24895         for(var i = 0, len = fs.length; i< len; i++){
24896             ff = fs[i];
24897             lc = ff.toLowerCase();
24898             buf.push(
24899                 '<option value="',lc,'" style="font-family:',ff,';"',
24900                     (this.defaultFont == lc ? ' selected="true">' : '>'),
24901                     ff,
24902                 '</option>'
24903             );
24904         }
24905         return buf.join('');
24906     },
24907     
24908     toggleSourceEdit : function(sourceEditMode){
24909         
24910         Roo.log("toolbar toogle");
24911         if(sourceEditMode === undefined){
24912             sourceEditMode = !this.sourceEditMode;
24913         }
24914         this.sourceEditMode = sourceEditMode === true;
24915         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
24916         // just toggle the button?
24917         if(btn.pressed !== this.sourceEditMode){
24918             btn.toggle(this.sourceEditMode);
24919             return;
24920         }
24921         
24922         if(sourceEditMode){
24923             Roo.log("disabling buttons");
24924             this.tb.items.each(function(item){
24925                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
24926                     item.disable();
24927                 }
24928             });
24929           
24930         }else{
24931             Roo.log("enabling buttons");
24932             if(this.editorcore.initialized){
24933                 this.tb.items.each(function(item){
24934                     item.enable();
24935                 });
24936             }
24937             
24938         }
24939         Roo.log("calling toggole on editor");
24940         // tell the editor that it's been pressed..
24941         this.editor.toggleSourceEdit(sourceEditMode);
24942        
24943     },
24944      /**
24945      * Object collection of toolbar tooltips for the buttons in the editor. The key
24946      * is the command id associated with that button and the value is a valid QuickTips object.
24947      * For example:
24948 <pre><code>
24949 {
24950     bold : {
24951         title: 'Bold (Ctrl+B)',
24952         text: 'Make the selected text bold.',
24953         cls: 'x-html-editor-tip'
24954     },
24955     italic : {
24956         title: 'Italic (Ctrl+I)',
24957         text: 'Make the selected text italic.',
24958         cls: 'x-html-editor-tip'
24959     },
24960     ...
24961 </code></pre>
24962     * @type Object
24963      */
24964     buttonTips : {
24965         bold : {
24966             title: 'Bold (Ctrl+B)',
24967             text: 'Make the selected text bold.',
24968             cls: 'x-html-editor-tip'
24969         },
24970         italic : {
24971             title: 'Italic (Ctrl+I)',
24972             text: 'Make the selected text italic.',
24973             cls: 'x-html-editor-tip'
24974         },
24975         underline : {
24976             title: 'Underline (Ctrl+U)',
24977             text: 'Underline the selected text.',
24978             cls: 'x-html-editor-tip'
24979         },
24980         strikethrough : {
24981             title: 'Strikethrough',
24982             text: 'Strikethrough the selected text.',
24983             cls: 'x-html-editor-tip'
24984         },
24985         increasefontsize : {
24986             title: 'Grow Text',
24987             text: 'Increase the font size.',
24988             cls: 'x-html-editor-tip'
24989         },
24990         decreasefontsize : {
24991             title: 'Shrink Text',
24992             text: 'Decrease the font size.',
24993             cls: 'x-html-editor-tip'
24994         },
24995         backcolor : {
24996             title: 'Text Highlight Color',
24997             text: 'Change the background color of the selected text.',
24998             cls: 'x-html-editor-tip'
24999         },
25000         forecolor : {
25001             title: 'Font Color',
25002             text: 'Change the color of the selected text.',
25003             cls: 'x-html-editor-tip'
25004         },
25005         justifyleft : {
25006             title: 'Align Text Left',
25007             text: 'Align text to the left.',
25008             cls: 'x-html-editor-tip'
25009         },
25010         justifycenter : {
25011             title: 'Center Text',
25012             text: 'Center text in the editor.',
25013             cls: 'x-html-editor-tip'
25014         },
25015         justifyright : {
25016             title: 'Align Text Right',
25017             text: 'Align text to the right.',
25018             cls: 'x-html-editor-tip'
25019         },
25020         insertunorderedlist : {
25021             title: 'Bullet List',
25022             text: 'Start a bulleted list.',
25023             cls: 'x-html-editor-tip'
25024         },
25025         insertorderedlist : {
25026             title: 'Numbered List',
25027             text: 'Start a numbered list.',
25028             cls: 'x-html-editor-tip'
25029         },
25030         createlink : {
25031             title: 'Hyperlink',
25032             text: 'Make the selected text a hyperlink.',
25033             cls: 'x-html-editor-tip'
25034         },
25035         sourceedit : {
25036             title: 'Source Edit',
25037             text: 'Switch to source editing mode.',
25038             cls: 'x-html-editor-tip'
25039         }
25040     },
25041     // private
25042     onDestroy : function(){
25043         if(this.rendered){
25044             
25045             this.tb.items.each(function(item){
25046                 if(item.menu){
25047                     item.menu.removeAll();
25048                     if(item.menu.el){
25049                         item.menu.el.destroy();
25050                     }
25051                 }
25052                 item.destroy();
25053             });
25054              
25055         }
25056     },
25057     onFirstFocus: function() {
25058         this.tb.items.each(function(item){
25059            item.enable();
25060         });
25061     }
25062 });
25063
25064
25065
25066
25067 // <script type="text/javascript">
25068 /*
25069  * Based on
25070  * Ext JS Library 1.1.1
25071  * Copyright(c) 2006-2007, Ext JS, LLC.
25072  *  
25073  
25074  */
25075
25076  
25077 /**
25078  * @class Roo.form.HtmlEditor.ToolbarContext
25079  * Context Toolbar
25080  * 
25081  * Usage:
25082  *
25083  new Roo.form.HtmlEditor({
25084     ....
25085     toolbars : [
25086         { xtype: 'ToolbarStandard', styles : {} }
25087         { xtype: 'ToolbarContext', disable : {} }
25088     ]
25089 })
25090
25091      
25092  * 
25093  * @config : {Object} disable List of elements to disable.. (not done yet.)
25094  * @config : {Object} styles  Map of styles available.
25095  * 
25096  */
25097
25098 Roo.form.HtmlEditor.ToolbarContext = function(config)
25099 {
25100     
25101     Roo.apply(this, config);
25102     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25103     // dont call parent... till later.
25104     this.styles = this.styles || {};
25105 }
25106
25107  
25108
25109 Roo.form.HtmlEditor.ToolbarContext.types = {
25110     'IMG' : {
25111         width : {
25112             title: "Width",
25113             width: 40
25114         },
25115         height:  {
25116             title: "Height",
25117             width: 40
25118         },
25119         align: {
25120             title: "Align",
25121             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25122             width : 80
25123             
25124         },
25125         border: {
25126             title: "Border",
25127             width: 40
25128         },
25129         alt: {
25130             title: "Alt",
25131             width: 120
25132         },
25133         src : {
25134             title: "Src",
25135             width: 220
25136         }
25137         
25138     },
25139     
25140     'FIGURE' : {
25141          align: {
25142             title: "Align",
25143             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25144             width : 80  
25145         }
25146     },
25147     'A' : {
25148         name : {
25149             title: "Name",
25150             width: 50
25151         },
25152         target:  {
25153             title: "Target",
25154             width: 120
25155         },
25156         href:  {
25157             title: "Href",
25158             width: 220
25159         } // border?
25160         
25161     },
25162     /*
25163     'TABLE' : {
25164         rows : {
25165             title: "Rows",
25166             width: 20
25167         },
25168         cols : {
25169             title: "Cols",
25170             width: 20
25171         },
25172         width : {
25173             title: "Width",
25174             width: 40
25175         },
25176         height : {
25177             title: "Height",
25178             width: 40
25179         },
25180         border : {
25181             title: "Border",
25182             width: 20
25183         }
25184     },
25185     'TD' : {
25186         width : {
25187             title: "Width",
25188             width: 40
25189         },
25190         height : {
25191             title: "Height",
25192             width: 40
25193         },   
25194         align: {
25195             title: "Align",
25196             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25197             width: 80
25198         },
25199         valign: {
25200             title: "Valign",
25201             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25202             width: 80
25203         },
25204         colspan: {
25205             title: "Colspan",
25206             width: 20
25207             
25208         },
25209          'font-family'  : {
25210             title : "Font",
25211             style : 'fontFamily',
25212             displayField: 'display',
25213             optname : 'font-family',
25214             width: 140
25215         }
25216     },
25217     */
25218     'INPUT' : {
25219         name : {
25220             title: "name",
25221             width: 120
25222         },
25223         value : {
25224             title: "Value",
25225             width: 120
25226         },
25227         width : {
25228             title: "Width",
25229             width: 40
25230         }
25231     },
25232     'LABEL' : {
25233         'for' : {
25234             title: "For",
25235             width: 120
25236         }
25237     },
25238     'TEXTAREA' : {
25239         name : {
25240             title: "name",
25241             width: 120
25242         },
25243         rows : {
25244             title: "Rows",
25245             width: 20
25246         },
25247         cols : {
25248             title: "Cols",
25249             width: 20
25250         }
25251     },
25252     'SELECT' : {
25253         name : {
25254             title: "name",
25255             width: 120
25256         },
25257         selectoptions : {
25258             title: "Options",
25259             width: 200
25260         }
25261     },
25262     
25263     // should we really allow this??
25264     // should this just be 
25265     'BODY' : {
25266         title : {
25267             title: "Title",
25268             width: 200,
25269             disabled : true
25270         }
25271     },
25272     /*
25273     'SPAN' : {
25274         'font-family'  : {
25275             title : "Font",
25276             style : 'fontFamily',
25277             displayField: 'display',
25278             optname : 'font-family',
25279             width: 140
25280         }
25281     },
25282     'DIV' : {
25283         'font-family'  : {
25284             title : "Font",
25285             style : 'fontFamily',
25286             displayField: 'display',
25287             optname : 'font-family',
25288             width: 140
25289         }
25290     },
25291      'P' : {
25292         'font-family'  : {
25293             title : "Font",
25294             style : 'fontFamily',
25295             displayField: 'display',
25296             optname : 'font-family',
25297             width: 140
25298         }
25299     },
25300     */
25301     '*' : {
25302         // empty..
25303     }
25304
25305 };
25306
25307 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
25308 Roo.form.HtmlEditor.ToolbarContext.stores = false;
25309
25310 Roo.form.HtmlEditor.ToolbarContext.options = {
25311         'font-family'  : [ 
25312                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
25313                 [ 'Courier New', 'Courier New'],
25314                 [ 'Tahoma', 'Tahoma'],
25315                 [ 'Times New Roman,serif', 'Times'],
25316                 [ 'Verdana','Verdana' ]
25317         ]
25318 };
25319
25320 // fixme - these need to be configurable..
25321  
25322
25323 //Roo.form.HtmlEditor.ToolbarContext.types
25324
25325
25326 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25327     
25328     tb: false,
25329     
25330     rendered: false,
25331     
25332     editor : false,
25333     editorcore : false,
25334     /**
25335      * @cfg {Object} disable  List of toolbar elements to disable
25336          
25337      */
25338     disable : false,
25339     /**
25340      * @cfg {Object} styles List of styles 
25341      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
25342      *
25343      * These must be defined in the page, so they get rendered correctly..
25344      * .headline { }
25345      * TD.underline { }
25346      * 
25347      */
25348     styles : false,
25349     
25350     options: false,
25351     
25352     toolbars : false,
25353     
25354     init : function(editor)
25355     {
25356         this.editor = editor;
25357         this.editorcore = editor.editorcore ? editor.editorcore : editor;
25358         var editorcore = this.editorcore;
25359         
25360         var fid = editorcore.frameId;
25361         var etb = this;
25362         function btn(id, toggle, handler){
25363             var xid = fid + '-'+ id ;
25364             return {
25365                 id : xid,
25366                 cmd : id,
25367                 cls : 'x-btn-icon x-edit-'+id,
25368                 enableToggle:toggle !== false,
25369                 scope: editorcore, // was editor...
25370                 handler:handler||editorcore.relayBtnCmd,
25371                 clickEvent:'mousedown',
25372                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25373                 tabIndex:-1
25374             };
25375         }
25376         // create a new element.
25377         var wdiv = editor.wrap.createChild({
25378                 tag: 'div'
25379             }, editor.wrap.dom.firstChild.nextSibling, true);
25380         
25381         // can we do this more than once??
25382         
25383          // stop form submits
25384       
25385  
25386         // disable everything...
25387         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25388         this.toolbars = {};
25389            
25390         for (var i in  ty) {
25391           
25392             this.toolbars[i] = this.buildToolbar(ty[i],i);
25393         }
25394         this.tb = this.toolbars.BODY;
25395         this.tb.el.show();
25396         this.buildFooter();
25397         this.footer.show();
25398         editor.on('hide', function( ) { this.footer.hide() }, this);
25399         editor.on('show', function( ) { this.footer.show() }, this);
25400         
25401          
25402         this.rendered = true;
25403         
25404         // the all the btns;
25405         editor.on('editorevent', this.updateToolbar, this);
25406         // other toolbars need to implement this..
25407         //editor.on('editmodechange', this.updateToolbar, this);
25408     },
25409     
25410     
25411     
25412     /**
25413      * Protected method that will not generally be called directly. It triggers
25414      * a toolbar update by reading the markup state of the current selection in the editor.
25415      *
25416      * Note you can force an update by calling on('editorevent', scope, false)
25417      */
25418     updateToolbar: function(editor ,ev, sel)
25419     {
25420
25421         //Roo.log(ev);
25422         // capture mouse up - this is handy for selecting images..
25423         // perhaps should go somewhere else...
25424         if(!this.editorcore.activated){
25425              this.editor.onFirstFocus();
25426             return;
25427         }
25428         
25429         
25430         
25431         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
25432         // selectNode - might want to handle IE?
25433         
25434         
25435         
25436         if (ev &&
25437             (ev.type == 'mouseup' || ev.type == 'click' ) &&
25438             ev.target && ev.target.tagName == 'IMG') {
25439             // they have click on an image...
25440             // let's see if we can change the selection...
25441             sel = ev.target;
25442             
25443             
25444             this.editorcore.selectNode(sel);
25445              
25446         }  
25447         
25448       
25449         //var updateFooter = sel ? false : true; 
25450         
25451         
25452         var ans = this.editorcore.getAllAncestors();
25453         
25454         // pick
25455         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
25456         
25457         if (!sel) { 
25458             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
25459             sel = sel ? sel : this.editorcore.doc.body;
25460             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
25461             
25462         }
25463         
25464         var tn = sel.tagName.toUpperCase();
25465         var lastSel = this.tb.selectedNode;
25466         this.tb.selectedNode = sel;
25467         var left_label = tn;
25468         
25469         // ok see if we are editing a block?
25470         var sel_el = Roo.get(sel);
25471         
25472         var db = sel_el.attr('data-block')  == undefined ?  sel_el.findParent('[data-block]') : sel;
25473         var cepar = Roo.get(sel).findParent('[contenteditable=true]');
25474         if (db && cepar && cepar.tagName != 'BODY') {
25475             db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
25476         }
25477         var block = false;
25478         if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
25479             block = Roo.htmleditor.Block.factory(db);
25480             if (block) {
25481                 tn = 'BLOCK.' + db.getAttribute('data-block');
25482                 this.tb.selectedNode = db;
25483                 this.editorcore.selectNode(db);
25484                 if (typeof(this.toolbars[tn]) == 'undefined') {
25485                    this.toolbars[tn] = this.buildToolbar( block.context || block.contextMenu(this) ,tn ,block.friendly_name);
25486                 }
25487                 left_label = block.friendly_name;
25488                 ans = this.editorcore.getAllAncestors();
25489             }
25490             
25491                 
25492             
25493         }
25494         
25495         
25496         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
25497             return; // no change?
25498         }
25499         
25500         
25501           
25502         this.tb.el.hide();
25503         ///console.log("show: " + tn);
25504         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
25505         
25506         this.tb.el.show();
25507         // update name
25508         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
25509         
25510         
25511         // update attributes
25512         if (block) {
25513              
25514             this.tb.fields.each(function(e) {
25515                 e.setValue(block[e.attrname]);
25516             });
25517             
25518             
25519         } else  if (this.tb.fields && this.tb.selectedNode) {
25520             this.tb.fields.each( function(e) {
25521                 if (e.stylename) {
25522                     e.setValue(this.tb.selectedNode.style[e.stylename]);
25523                     return;
25524                 } 
25525                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
25526             }, this);
25527             this.updateToolbarStyles(this.tb.selectedNode);  
25528         }
25529         
25530         
25531        
25532         Roo.menu.MenuMgr.hideAll();
25533
25534         
25535         
25536     
25537         // update the footer
25538         //
25539         this.updateFooter(ans);
25540              
25541     },
25542     
25543     updateToolbarStyles : function(sel)
25544     {
25545         var hasStyles = false;
25546         for(var i in this.styles) {
25547             hasStyles = true;
25548             break;
25549         }
25550         
25551         // update styles
25552         if (hasStyles && this.tb.hasStyles) { 
25553             var st = this.tb.fields.item(0);
25554             
25555             st.store.removeAll();
25556             var cn = sel.className.split(/\s+/);
25557             
25558             var avs = [];
25559             if (this.styles['*']) {
25560                 
25561                 Roo.each(this.styles['*'], function(v) {
25562                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
25563                 });
25564             }
25565             if (this.styles[tn]) { 
25566                 Roo.each(this.styles[tn], function(v) {
25567                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
25568                 });
25569             }
25570             
25571             st.store.loadData(avs);
25572             st.collapse();
25573             st.setValue(cn);
25574         }
25575     },
25576     
25577      
25578     updateFooter : function(ans)
25579     {
25580         var html = '';
25581         if (ans === false) {
25582             this.footDisp.dom.innerHTML = '';
25583             return;
25584         }
25585         
25586         this.footerEls = ans.reverse();
25587         Roo.each(this.footerEls, function(a,i) {
25588             if (!a) { return; }
25589             html += html.length ? ' &gt; '  :  '';
25590             
25591             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
25592             
25593         });
25594        
25595         // 
25596         var sz = this.footDisp.up('td').getSize();
25597         this.footDisp.dom.style.width = (sz.width -10) + 'px';
25598         this.footDisp.dom.style.marginLeft = '5px';
25599         
25600         this.footDisp.dom.style.overflow = 'hidden';
25601         
25602         this.footDisp.dom.innerHTML = html;
25603             
25604         
25605     },
25606    
25607        
25608     // private
25609     onDestroy : function(){
25610         if(this.rendered){
25611             
25612             this.tb.items.each(function(item){
25613                 if(item.menu){
25614                     item.menu.removeAll();
25615                     if(item.menu.el){
25616                         item.menu.el.destroy();
25617                     }
25618                 }
25619                 item.destroy();
25620             });
25621              
25622         }
25623     },
25624     onFirstFocus: function() {
25625         // need to do this for all the toolbars..
25626         this.tb.items.each(function(item){
25627            item.enable();
25628         });
25629     },
25630     buildToolbar: function(tlist, nm, friendly_name)
25631     {
25632         var editor = this.editor;
25633         var editorcore = this.editorcore;
25634          // create a new element.
25635         var wdiv = editor.wrap.createChild({
25636                 tag: 'div'
25637             }, editor.wrap.dom.firstChild.nextSibling, true);
25638         
25639        
25640         var tb = new Roo.Toolbar(wdiv);
25641         tb.hasStyles = false;
25642         tb.name = nm;
25643         
25644         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
25645         
25646         var styles = Array.from(this.styles);
25647         
25648         
25649         // styles...
25650         if (styles && styles.length) {
25651             tb.hasStyles = true;
25652             // this needs a multi-select checkbox...
25653             tb.addField( new Roo.form.ComboBox({
25654                 store: new Roo.data.SimpleStore({
25655                     id : 'val',
25656                     fields: ['val', 'selected'],
25657                     data : [] 
25658                 }),
25659                 name : '-roo-edit-className',
25660                 attrname : 'className',
25661                 displayField: 'val',
25662                 typeAhead: false,
25663                 mode: 'local',
25664                 editable : false,
25665                 triggerAction: 'all',
25666                 emptyText:'Select Style',
25667                 selectOnFocus:true,
25668                 width: 130,
25669                 listeners : {
25670                     'select': function(c, r, i) {
25671                         // initial support only for on class per el..
25672                         tb.selectedNode.className =  r ? r.get('val') : '';
25673                         editorcore.syncValue();
25674                     }
25675                 }
25676     
25677             }));
25678         }
25679         
25680         var tbc = Roo.form.HtmlEditor.ToolbarContext;
25681         
25682         
25683         for (var i in tlist) {
25684             
25685             // newer versions will use xtype cfg to create menus.
25686             if (typeof(tlist[i].xtype) != 'undefined') {
25687                 tb.add(Roo.factory(tlist[i]));
25688                 continue;
25689             }
25690             
25691             var item = tlist[i];
25692             tb.add(item.title + ":&nbsp;");
25693             
25694             
25695             //optname == used so you can configure the options available..
25696             var opts = item.opts ? item.opts : false;
25697             if (item.optname) { // use the b
25698                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
25699            
25700             }
25701             
25702             if (opts) {
25703                 // opts == pulldown..
25704                 tb.addField( new Roo.form.ComboBox({
25705                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
25706                         id : 'val',
25707                         fields: ['val', 'display'],
25708                         data : opts  
25709                     }),
25710                     name : '-roo-edit-' + i,
25711                     
25712                     attrname : i,
25713                     stylename : item.style ? item.style : false,
25714                     
25715                     displayField: item.displayField ? item.displayField : 'val',
25716                     valueField :  'val',
25717                     typeAhead: false,
25718                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
25719                     editable : false,
25720                     triggerAction: 'all',
25721                     emptyText:'Select',
25722                     selectOnFocus:true,
25723                     width: item.width ? item.width  : 130,
25724                     listeners : {
25725                         'select': function(c, r, i) {
25726                             if (tb.selectedNode.hasAttribute('data-block')) {
25727                                 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
25728                                 b[c.attrname] = r.get('val');
25729                                 b.updateElement(tb.selectedNode);
25730                                 editorcore.syncValue();
25731                                 return;
25732                             }
25733                             
25734                             if (c.stylename) {
25735                                 tb.selectedNode.style[c.stylename] =  r.get('val');
25736                                 editorcore.syncValue();
25737                                 return;
25738                             }
25739                             if (r === false) {
25740                                 tb.selectedNode.removeAttribute(c.attrname);
25741                                 editorcore.syncValue();
25742                                 return;
25743                             }
25744                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
25745                             editorcore.syncValue();
25746                         }
25747                     }
25748
25749                 }));
25750                 continue;
25751                     
25752                  
25753                 /*
25754                 tb.addField( new Roo.form.TextField({
25755                     name: i,
25756                     width: 100,
25757                     //allowBlank:false,
25758                     value: ''
25759                 }));
25760                 continue;
25761                 */
25762             }
25763             tb.addField( new Roo.form.TextField({
25764                 name: '-roo-edit-' + i,
25765                 attrname : i,
25766                 
25767                 width: item.width,
25768                 //allowBlank:true,
25769                 value: '',
25770                 listeners: {
25771                     'change' : function(f, nv, ov) {
25772                         
25773                         if (tb.selectedNode.hasAttribute('data-block')) {
25774                             var b = Roo.htmleditor.Block.factory(tb.selectedNode);
25775                             b[f.attrname] = nv;
25776                             b.updateElement(tb.selectedNode);
25777                             editorcore.syncValue();
25778                             return;
25779                         }
25780                         
25781                         tb.selectedNode.setAttribute(f.attrname, nv);
25782                         editorcore.syncValue();
25783                     }
25784                 }
25785             }));
25786              
25787         }
25788         
25789         var _this = this;
25790         
25791         if(nm == 'BODY'){
25792             tb.addSeparator();
25793         
25794             tb.addButton( {
25795                 text: 'Stylesheets',
25796
25797                 listeners : {
25798                     click : function ()
25799                     {
25800                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
25801                     }
25802                 }
25803             });
25804         }
25805         
25806         tb.addFill();
25807         tb.addButton({
25808             text: 'Remove Block or Formating', // remove the tag, and puts the children outside...
25809     
25810             listeners : {
25811                 click : function ()
25812                 {
25813                     // remove
25814                     // undo does not work.
25815                     var sn = tb.selectedNode;
25816                     if (!sn) {
25817                         return;
25818                     }
25819                     var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
25820                     if (sn.hasAttribute('data-block')) {
25821                         stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
25822                         sn.parentNode.removeChild(sn);
25823                         
25824                     } else if (sn && sn.tagName != 'BODY') {
25825                         // remove and keep parents.
25826                         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
25827                         a.removeTag(sn);
25828                     }
25829                     
25830                     
25831                     var range = editorcore.createRange();
25832         
25833                     range.setStart(stn,0);
25834                     range.setEnd(stn,0); 
25835                     var selection = editorcore.getSelection();
25836                     selection.removeAllRanges();
25837                     selection.addRange(range);
25838                     
25839                     
25840                     //_this.updateToolbar(null, null, pn);
25841                     _this.updateToolbar(null, null, null);
25842                     _this.updateFooter(false);
25843                     
25844                 }
25845             }
25846             
25847                     
25848                 
25849             
25850         });
25851         
25852         
25853         tb.el.on('click', function(e){
25854             e.preventDefault(); // what does this do?
25855         });
25856         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25857         tb.el.hide();
25858         
25859         // dont need to disable them... as they will get hidden
25860         return tb;
25861          
25862         
25863     },
25864     buildFooter : function()
25865     {
25866         
25867         var fel = this.editor.wrap.createChild();
25868         this.footer = new Roo.Toolbar(fel);
25869         // toolbar has scrolly on left / right?
25870         var footDisp= new Roo.Toolbar.Fill();
25871         var _t = this;
25872         this.footer.add(
25873             {
25874                 text : '&lt;',
25875                 xtype: 'Button',
25876                 handler : function() {
25877                     _t.footDisp.scrollTo('left',0,true)
25878                 }
25879             }
25880         );
25881         this.footer.add( footDisp );
25882         this.footer.add( 
25883             {
25884                 text : '&gt;',
25885                 xtype: 'Button',
25886                 handler : function() {
25887                     // no animation..
25888                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
25889                 }
25890             }
25891         );
25892         var fel = Roo.get(footDisp.el);
25893         fel.addClass('x-editor-context');
25894         this.footDispWrap = fel; 
25895         this.footDispWrap.overflow  = 'hidden';
25896         
25897         this.footDisp = fel.createChild();
25898         this.footDispWrap.on('click', this.onContextClick, this)
25899         
25900         
25901     },
25902     // when the footer contect changes
25903     onContextClick : function (ev,dom)
25904     {
25905         ev.preventDefault();
25906         var  cn = dom.className;
25907         //Roo.log(cn);
25908         if (!cn.match(/x-ed-loc-/)) {
25909             return;
25910         }
25911         var n = cn.split('-').pop();
25912         var ans = this.footerEls;
25913         var sel = ans[n];
25914         
25915          // pick
25916         var range = this.editorcore.createRange();
25917         
25918         range.selectNodeContents(sel);
25919         //range.selectNode(sel);
25920         
25921         
25922         var selection = this.editorcore.getSelection();
25923         selection.removeAllRanges();
25924         selection.addRange(range);
25925         
25926         
25927         
25928         this.updateToolbar(null, null, sel);
25929         
25930         
25931     }
25932     
25933     
25934     
25935     
25936     
25937 });
25938
25939
25940
25941
25942
25943 /*
25944  * Based on:
25945  * Ext JS Library 1.1.1
25946  * Copyright(c) 2006-2007, Ext JS, LLC.
25947  *
25948  * Originally Released Under LGPL - original licence link has changed is not relivant.
25949  *
25950  * Fork - LGPL
25951  * <script type="text/javascript">
25952  */
25953  
25954 /**
25955  * @class Roo.form.BasicForm
25956  * @extends Roo.util.Observable
25957  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25958  * @constructor
25959  * @param {String/HTMLElement/Roo.Element} el The form element or its id
25960  * @param {Object} config Configuration options
25961  */
25962 Roo.form.BasicForm = function(el, config){
25963     this.allItems = [];
25964     this.childForms = [];
25965     Roo.apply(this, config);
25966     /*
25967      * The Roo.form.Field items in this form.
25968      * @type MixedCollection
25969      */
25970      
25971      
25972     this.items = new Roo.util.MixedCollection(false, function(o){
25973         return o.id || (o.id = Roo.id());
25974     });
25975     this.addEvents({
25976         /**
25977          * @event beforeaction
25978          * Fires before any action is performed. Return false to cancel the action.
25979          * @param {Form} this
25980          * @param {Action} action The action to be performed
25981          */
25982         beforeaction: true,
25983         /**
25984          * @event actionfailed
25985          * Fires when an action fails.
25986          * @param {Form} this
25987          * @param {Action} action The action that failed
25988          */
25989         actionfailed : true,
25990         /**
25991          * @event actioncomplete
25992          * Fires when an action is completed.
25993          * @param {Form} this
25994          * @param {Action} action The action that completed
25995          */
25996         actioncomplete : true
25997     });
25998     if(el){
25999         this.initEl(el);
26000     }
26001     Roo.form.BasicForm.superclass.constructor.call(this);
26002     
26003     Roo.form.BasicForm.popover.apply();
26004 };
26005
26006 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
26007     /**
26008      * @cfg {String} method
26009      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
26010      */
26011     /**
26012      * @cfg {DataReader} reader
26013      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
26014      * This is optional as there is built-in support for processing JSON.
26015      */
26016     /**
26017      * @cfg {DataReader} errorReader
26018      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
26019      * This is completely optional as there is built-in support for processing JSON.
26020      */
26021     /**
26022      * @cfg {String} url
26023      * The URL to use for form actions if one isn't supplied in the action options.
26024      */
26025     /**
26026      * @cfg {Boolean} fileUpload
26027      * Set to true if this form is a file upload.
26028      */
26029      
26030     /**
26031      * @cfg {Object} baseParams
26032      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
26033      */
26034      /**
26035      
26036     /**
26037      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
26038      */
26039     timeout: 30,
26040
26041     // private
26042     activeAction : null,
26043
26044     /**
26045      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
26046      * or setValues() data instead of when the form was first created.
26047      */
26048     trackResetOnLoad : false,
26049     
26050     
26051     /**
26052      * childForms - used for multi-tab forms
26053      * @type {Array}
26054      */
26055     childForms : false,
26056     
26057     /**
26058      * allItems - full list of fields.
26059      * @type {Array}
26060      */
26061     allItems : false,
26062     
26063     /**
26064      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26065      * element by passing it or its id or mask the form itself by passing in true.
26066      * @type Mixed
26067      */
26068     waitMsgTarget : false,
26069     
26070     /**
26071      * @type Boolean
26072      */
26073     disableMask : false,
26074     
26075     /**
26076      * @cfg {Boolean} errorMask (true|false) default false
26077      */
26078     errorMask : false,
26079     
26080     /**
26081      * @cfg {Number} maskOffset Default 100
26082      */
26083     maskOffset : 100,
26084
26085     // private
26086     initEl : function(el){
26087         this.el = Roo.get(el);
26088         this.id = this.el.id || Roo.id();
26089         this.el.on('submit', this.onSubmit, this);
26090         this.el.addClass('x-form');
26091     },
26092
26093     // private
26094     onSubmit : function(e){
26095         e.stopEvent();
26096     },
26097
26098     /**
26099      * Returns true if client-side validation on the form is successful.
26100      * @return Boolean
26101      */
26102     isValid : function(){
26103         var valid = true;
26104         var target = false;
26105         this.items.each(function(f){
26106             if(f.validate()){
26107                 return;
26108             }
26109             
26110             valid = false;
26111                 
26112             if(!target && f.el.isVisible(true)){
26113                 target = f;
26114             }
26115         });
26116         
26117         if(this.errorMask && !valid){
26118             Roo.form.BasicForm.popover.mask(this, target);
26119         }
26120         
26121         return valid;
26122     },
26123     /**
26124      * Returns array of invalid form fields.
26125      * @return Array
26126      */
26127     
26128     invalidFields : function()
26129     {
26130         var ret = [];
26131         this.items.each(function(f){
26132             if(f.validate()){
26133                 return;
26134             }
26135             ret.push(f);
26136             
26137         });
26138         
26139         return ret;
26140     },
26141     
26142     
26143     /**
26144      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
26145      * @return Boolean
26146      */
26147     isDirty : function(){
26148         var dirty = false;
26149         this.items.each(function(f){
26150            if(f.isDirty()){
26151                dirty = true;
26152                return false;
26153            }
26154         });
26155         return dirty;
26156     },
26157     
26158     /**
26159      * Returns true if any fields in this form have changed since their original load. (New version)
26160      * @return Boolean
26161      */
26162     
26163     hasChanged : function()
26164     {
26165         var dirty = false;
26166         this.items.each(function(f){
26167            if(f.hasChanged()){
26168                dirty = true;
26169                return false;
26170            }
26171         });
26172         return dirty;
26173         
26174     },
26175     /**
26176      * Resets all hasChanged to 'false' -
26177      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
26178      * So hasChanged storage is only to be used for this purpose
26179      * @return Boolean
26180      */
26181     resetHasChanged : function()
26182     {
26183         this.items.each(function(f){
26184            f.resetHasChanged();
26185         });
26186         
26187     },
26188     
26189     
26190     /**
26191      * Performs a predefined action (submit or load) or custom actions you define on this form.
26192      * @param {String} actionName The name of the action type
26193      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26194      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26195      * accept other config options):
26196      * <pre>
26197 Property          Type             Description
26198 ----------------  ---------------  ----------------------------------------------------------------------------------
26199 url               String           The url for the action (defaults to the form's url)
26200 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26201 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26202 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26203                                    validate the form on the client (defaults to false)
26204      * </pre>
26205      * @return {BasicForm} this
26206      */
26207     doAction : function(action, options){
26208         if(typeof action == 'string'){
26209             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26210         }
26211         if(this.fireEvent('beforeaction', this, action) !== false){
26212             this.beforeAction(action);
26213             action.run.defer(100, action);
26214         }
26215         return this;
26216     },
26217
26218     /**
26219      * Shortcut to do a submit action.
26220      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26221      * @return {BasicForm} this
26222      */
26223     submit : function(options){
26224         this.doAction('submit', options);
26225         return this;
26226     },
26227
26228     /**
26229      * Shortcut to do a load action.
26230      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26231      * @return {BasicForm} this
26232      */
26233     load : function(options){
26234         this.doAction('load', options);
26235         return this;
26236     },
26237
26238     /**
26239      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26240      * @param {Record} record The record to edit
26241      * @return {BasicForm} this
26242      */
26243     updateRecord : function(record){
26244         record.beginEdit();
26245         var fs = record.fields;
26246         fs.each(function(f){
26247             var field = this.findField(f.name);
26248             if(field){
26249                 record.set(f.name, field.getValue());
26250             }
26251         }, this);
26252         record.endEdit();
26253         return this;
26254     },
26255
26256     /**
26257      * Loads an Roo.data.Record into this form.
26258      * @param {Record} record The record to load
26259      * @return {BasicForm} this
26260      */
26261     loadRecord : function(record){
26262         this.setValues(record.data);
26263         return this;
26264     },
26265
26266     // private
26267     beforeAction : function(action){
26268         var o = action.options;
26269         
26270         if(!this.disableMask) {
26271             if(this.waitMsgTarget === true){
26272                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
26273             }else if(this.waitMsgTarget){
26274                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26275                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
26276             }else {
26277                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
26278             }
26279         }
26280         
26281          
26282     },
26283
26284     // private
26285     afterAction : function(action, success){
26286         this.activeAction = null;
26287         var o = action.options;
26288         
26289         if(!this.disableMask) {
26290             if(this.waitMsgTarget === true){
26291                 this.el.unmask();
26292             }else if(this.waitMsgTarget){
26293                 this.waitMsgTarget.unmask();
26294             }else{
26295                 Roo.MessageBox.updateProgress(1);
26296                 Roo.MessageBox.hide();
26297             }
26298         }
26299         
26300         if(success){
26301             if(o.reset){
26302                 this.reset();
26303             }
26304             Roo.callback(o.success, o.scope, [this, action]);
26305             this.fireEvent('actioncomplete', this, action);
26306             
26307         }else{
26308             
26309             // failure condition..
26310             // we have a scenario where updates need confirming.
26311             // eg. if a locking scenario exists..
26312             // we look for { errors : { needs_confirm : true }} in the response.
26313             if (
26314                 (typeof(action.result) != 'undefined')  &&
26315                 (typeof(action.result.errors) != 'undefined')  &&
26316                 (typeof(action.result.errors.needs_confirm) != 'undefined')
26317            ){
26318                 var _t = this;
26319                 Roo.MessageBox.confirm(
26320                     "Change requires confirmation",
26321                     action.result.errorMsg,
26322                     function(r) {
26323                         if (r != 'yes') {
26324                             return;
26325                         }
26326                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
26327                     }
26328                     
26329                 );
26330                 
26331                 
26332                 
26333                 return;
26334             }
26335             
26336             Roo.callback(o.failure, o.scope, [this, action]);
26337             // show an error message if no failed handler is set..
26338             if (!this.hasListener('actionfailed')) {
26339                 Roo.MessageBox.alert("Error",
26340                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
26341                         action.result.errorMsg :
26342                         "Saving Failed, please check your entries or try again"
26343                 );
26344             }
26345             
26346             this.fireEvent('actionfailed', this, action);
26347         }
26348         
26349     },
26350
26351     /**
26352      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26353      * @param {String} id The value to search for
26354      * @return Field
26355      */
26356     findField : function(id){
26357         var field = this.items.get(id);
26358         if(!field){
26359             this.items.each(function(f){
26360                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26361                     field = f;
26362                     return false;
26363                 }
26364             });
26365         }
26366         return field || null;
26367     },
26368
26369     /**
26370      * Add a secondary form to this one, 
26371      * Used to provide tabbed forms. One form is primary, with hidden values 
26372      * which mirror the elements from the other forms.
26373      * 
26374      * @param {Roo.form.Form} form to add.
26375      * 
26376      */
26377     addForm : function(form)
26378     {
26379        
26380         if (this.childForms.indexOf(form) > -1) {
26381             // already added..
26382             return;
26383         }
26384         this.childForms.push(form);
26385         var n = '';
26386         Roo.each(form.allItems, function (fe) {
26387             
26388             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26389             if (this.findField(n)) { // already added..
26390                 return;
26391             }
26392             var add = new Roo.form.Hidden({
26393                 name : n
26394             });
26395             add.render(this.el);
26396             
26397             this.add( add );
26398         }, this);
26399         
26400     },
26401     /**
26402      * Mark fields in this form invalid in bulk.
26403      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26404      * @return {BasicForm} this
26405      */
26406     markInvalid : function(errors){
26407         if(errors instanceof Array){
26408             for(var i = 0, len = errors.length; i < len; i++){
26409                 var fieldError = errors[i];
26410                 var f = this.findField(fieldError.id);
26411                 if(f){
26412                     f.markInvalid(fieldError.msg);
26413                 }
26414             }
26415         }else{
26416             var field, id;
26417             for(id in errors){
26418                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26419                     field.markInvalid(errors[id]);
26420                 }
26421             }
26422         }
26423         Roo.each(this.childForms || [], function (f) {
26424             f.markInvalid(errors);
26425         });
26426         
26427         return this;
26428     },
26429
26430     /**
26431      * Set values for fields in this form in bulk.
26432      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26433      * @return {BasicForm} this
26434      */
26435     setValues : function(values){
26436         if(values instanceof Array){ // array of objects
26437             for(var i = 0, len = values.length; i < len; i++){
26438                 var v = values[i];
26439                 var f = this.findField(v.id);
26440                 if(f){
26441                     f.setValue(v.value);
26442                     if(this.trackResetOnLoad){
26443                         f.originalValue = f.getValue();
26444                     }
26445                 }
26446             }
26447         }else{ // object hash
26448             var field, id;
26449             for(id in values){
26450                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26451                     
26452                     if (field.setFromData && 
26453                         field.valueField && 
26454                         field.displayField &&
26455                         // combos' with local stores can 
26456                         // be queried via setValue()
26457                         // to set their value..
26458                         (field.store && !field.store.isLocal)
26459                         ) {
26460                         // it's a combo
26461                         var sd = { };
26462                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
26463                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
26464                         field.setFromData(sd);
26465                         
26466                     } else {
26467                         field.setValue(values[id]);
26468                     }
26469                     
26470                     
26471                     if(this.trackResetOnLoad){
26472                         field.originalValue = field.getValue();
26473                     }
26474                 }
26475             }
26476         }
26477         this.resetHasChanged();
26478         
26479         
26480         Roo.each(this.childForms || [], function (f) {
26481             f.setValues(values);
26482             f.resetHasChanged();
26483         });
26484                 
26485         return this;
26486     },
26487  
26488     /**
26489      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
26490      * they are returned as an array.
26491      * @param {Boolean} asString
26492      * @return {Object}
26493      */
26494     getValues : function(asString){
26495         if (this.childForms) {
26496             // copy values from the child forms
26497             Roo.each(this.childForms, function (f) {
26498                 this.setValues(f.getValues());
26499             }, this);
26500         }
26501         
26502         // use formdata
26503         if (typeof(FormData) != 'undefined' && asString !== true) {
26504             // this relies on a 'recent' version of chrome apparently...
26505             try {
26506                 var fd = (new FormData(this.el.dom)).entries();
26507                 var ret = {};
26508                 var ent = fd.next();
26509                 while (!ent.done) {
26510                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
26511                     ent = fd.next();
26512                 };
26513                 return ret;
26514             } catch(e) {
26515                 
26516             }
26517             
26518         }
26519         
26520         
26521         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
26522         if(asString === true){
26523             return fs;
26524         }
26525         return Roo.urlDecode(fs);
26526     },
26527     
26528     /**
26529      * Returns the fields in this form as an object with key/value pairs. 
26530      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
26531      * @return {Object}
26532      */
26533     getFieldValues : function(with_hidden)
26534     {
26535         if (this.childForms) {
26536             // copy values from the child forms
26537             // should this call getFieldValues - probably not as we do not currently copy
26538             // hidden fields when we generate..
26539             Roo.each(this.childForms, function (f) {
26540                 this.setValues(f.getValues());
26541             }, this);
26542         }
26543         
26544         var ret = {};
26545         this.items.each(function(f){
26546             if (!f.getName()) {
26547                 return;
26548             }
26549             var v = f.getValue();
26550             if (f.inputType =='radio') {
26551                 if (typeof(ret[f.getName()]) == 'undefined') {
26552                     ret[f.getName()] = ''; // empty..
26553                 }
26554                 
26555                 if (!f.el.dom.checked) {
26556                     return;
26557                     
26558                 }
26559                 v = f.el.dom.value;
26560                 
26561             }
26562             
26563             // not sure if this supported any more..
26564             if ((typeof(v) == 'object') && f.getRawValue) {
26565                 v = f.getRawValue() ; // dates..
26566             }
26567             // combo boxes where name != hiddenName...
26568             if (f.name != f.getName()) {
26569                 ret[f.name] = f.getRawValue();
26570             }
26571             ret[f.getName()] = v;
26572         });
26573         
26574         return ret;
26575     },
26576
26577     /**
26578      * Clears all invalid messages in this form.
26579      * @return {BasicForm} this
26580      */
26581     clearInvalid : function(){
26582         this.items.each(function(f){
26583            f.clearInvalid();
26584         });
26585         
26586         Roo.each(this.childForms || [], function (f) {
26587             f.clearInvalid();
26588         });
26589         
26590         
26591         return this;
26592     },
26593
26594     /**
26595      * Resets this form.
26596      * @return {BasicForm} this
26597      */
26598     reset : function(){
26599         this.items.each(function(f){
26600             f.reset();
26601         });
26602         
26603         Roo.each(this.childForms || [], function (f) {
26604             f.reset();
26605         });
26606         this.resetHasChanged();
26607         
26608         return this;
26609     },
26610
26611     /**
26612      * Add Roo.form components to this form.
26613      * @param {Field} field1
26614      * @param {Field} field2 (optional)
26615      * @param {Field} etc (optional)
26616      * @return {BasicForm} this
26617      */
26618     add : function(){
26619         this.items.addAll(Array.prototype.slice.call(arguments, 0));
26620         return this;
26621     },
26622
26623
26624     /**
26625      * Removes a field from the items collection (does NOT remove its markup).
26626      * @param {Field} field
26627      * @return {BasicForm} this
26628      */
26629     remove : function(field){
26630         this.items.remove(field);
26631         return this;
26632     },
26633
26634     /**
26635      * Looks at the fields in this form, checks them for an id attribute,
26636      * and calls applyTo on the existing dom element with that id.
26637      * @return {BasicForm} this
26638      */
26639     render : function(){
26640         this.items.each(function(f){
26641             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
26642                 f.applyTo(f.id);
26643             }
26644         });
26645         return this;
26646     },
26647
26648     /**
26649      * Calls {@link Ext#apply} for all fields in this form with the passed object.
26650      * @param {Object} values
26651      * @return {BasicForm} this
26652      */
26653     applyToFields : function(o){
26654         this.items.each(function(f){
26655            Roo.apply(f, o);
26656         });
26657         return this;
26658     },
26659
26660     /**
26661      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
26662      * @param {Object} values
26663      * @return {BasicForm} this
26664      */
26665     applyIfToFields : function(o){
26666         this.items.each(function(f){
26667            Roo.applyIf(f, o);
26668         });
26669         return this;
26670     }
26671 });
26672
26673 // back compat
26674 Roo.BasicForm = Roo.form.BasicForm;
26675
26676 Roo.apply(Roo.form.BasicForm, {
26677     
26678     popover : {
26679         
26680         padding : 5,
26681         
26682         isApplied : false,
26683         
26684         isMasked : false,
26685         
26686         form : false,
26687         
26688         target : false,
26689         
26690         intervalID : false,
26691         
26692         maskEl : false,
26693         
26694         apply : function()
26695         {
26696             if(this.isApplied){
26697                 return;
26698             }
26699             
26700             this.maskEl = {
26701                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
26702                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
26703                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
26704                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
26705             };
26706             
26707             this.maskEl.top.enableDisplayMode("block");
26708             this.maskEl.left.enableDisplayMode("block");
26709             this.maskEl.bottom.enableDisplayMode("block");
26710             this.maskEl.right.enableDisplayMode("block");
26711             
26712             Roo.get(document.body).on('click', function(){
26713                 this.unmask();
26714             }, this);
26715             
26716             Roo.get(document.body).on('touchstart', function(){
26717                 this.unmask();
26718             }, this);
26719             
26720             this.isApplied = true
26721         },
26722         
26723         mask : function(form, target)
26724         {
26725             this.form = form;
26726             
26727             this.target = target;
26728             
26729             if(!this.form.errorMask || !target.el){
26730                 return;
26731             }
26732             
26733             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
26734             
26735             var ot = this.target.el.calcOffsetsTo(scrollable);
26736             
26737             var scrollTo = ot[1] - this.form.maskOffset;
26738             
26739             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
26740             
26741             scrollable.scrollTo('top', scrollTo);
26742             
26743             var el = this.target.wrap || this.target.el;
26744             
26745             var box = el.getBox();
26746             
26747             this.maskEl.top.setStyle('position', 'absolute');
26748             this.maskEl.top.setStyle('z-index', 10000);
26749             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
26750             this.maskEl.top.setLeft(0);
26751             this.maskEl.top.setTop(0);
26752             this.maskEl.top.show();
26753             
26754             this.maskEl.left.setStyle('position', 'absolute');
26755             this.maskEl.left.setStyle('z-index', 10000);
26756             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
26757             this.maskEl.left.setLeft(0);
26758             this.maskEl.left.setTop(box.y - this.padding);
26759             this.maskEl.left.show();
26760
26761             this.maskEl.bottom.setStyle('position', 'absolute');
26762             this.maskEl.bottom.setStyle('z-index', 10000);
26763             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
26764             this.maskEl.bottom.setLeft(0);
26765             this.maskEl.bottom.setTop(box.bottom + this.padding);
26766             this.maskEl.bottom.show();
26767
26768             this.maskEl.right.setStyle('position', 'absolute');
26769             this.maskEl.right.setStyle('z-index', 10000);
26770             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
26771             this.maskEl.right.setLeft(box.right + this.padding);
26772             this.maskEl.right.setTop(box.y - this.padding);
26773             this.maskEl.right.show();
26774
26775             this.intervalID = window.setInterval(function() {
26776                 Roo.form.BasicForm.popover.unmask();
26777             }, 10000);
26778
26779             window.onwheel = function(){ return false;};
26780             
26781             (function(){ this.isMasked = true; }).defer(500, this);
26782             
26783         },
26784         
26785         unmask : function()
26786         {
26787             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
26788                 return;
26789             }
26790             
26791             this.maskEl.top.setStyle('position', 'absolute');
26792             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
26793             this.maskEl.top.hide();
26794
26795             this.maskEl.left.setStyle('position', 'absolute');
26796             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
26797             this.maskEl.left.hide();
26798
26799             this.maskEl.bottom.setStyle('position', 'absolute');
26800             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
26801             this.maskEl.bottom.hide();
26802
26803             this.maskEl.right.setStyle('position', 'absolute');
26804             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
26805             this.maskEl.right.hide();
26806             
26807             window.onwheel = function(){ return true;};
26808             
26809             if(this.intervalID){
26810                 window.clearInterval(this.intervalID);
26811                 this.intervalID = false;
26812             }
26813             
26814             this.isMasked = false;
26815             
26816         }
26817         
26818     }
26819     
26820 });/*
26821  * Based on:
26822  * Ext JS Library 1.1.1
26823  * Copyright(c) 2006-2007, Ext JS, LLC.
26824  *
26825  * Originally Released Under LGPL - original licence link has changed is not relivant.
26826  *
26827  * Fork - LGPL
26828  * <script type="text/javascript">
26829  */
26830
26831 /**
26832  * @class Roo.form.Form
26833  * @extends Roo.form.BasicForm
26834  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26835  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
26836  * @constructor
26837  * @param {Object} config Configuration options
26838  */
26839 Roo.form.Form = function(config){
26840     var xitems =  [];
26841     if (config.items) {
26842         xitems = config.items;
26843         delete config.items;
26844     }
26845    
26846     
26847     Roo.form.Form.superclass.constructor.call(this, null, config);
26848     this.url = this.url || this.action;
26849     if(!this.root){
26850         this.root = new Roo.form.Layout(Roo.applyIf({
26851             id: Roo.id()
26852         }, config));
26853     }
26854     this.active = this.root;
26855     /**
26856      * Array of all the buttons that have been added to this form via {@link addButton}
26857      * @type Array
26858      */
26859     this.buttons = [];
26860     this.allItems = [];
26861     this.addEvents({
26862         /**
26863          * @event clientvalidation
26864          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
26865          * @param {Form} this
26866          * @param {Boolean} valid true if the form has passed client-side validation
26867          */
26868         clientvalidation: true,
26869         /**
26870          * @event rendered
26871          * Fires when the form is rendered
26872          * @param {Roo.form.Form} form
26873          */
26874         rendered : true
26875     });
26876     
26877     if (this.progressUrl) {
26878             // push a hidden field onto the list of fields..
26879             this.addxtype( {
26880                     xns: Roo.form, 
26881                     xtype : 'Hidden', 
26882                     name : 'UPLOAD_IDENTIFIER' 
26883             });
26884         }
26885         
26886     
26887     Roo.each(xitems, this.addxtype, this);
26888     
26889 };
26890
26891 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
26892      /**
26893      * @cfg {Roo.Button} buttons[] buttons at bottom of form
26894      */
26895     
26896     /**
26897      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
26898      */
26899     /**
26900      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
26901      */
26902     /**
26903      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
26904      */
26905     buttonAlign:'center',
26906
26907     /**
26908      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
26909      */
26910     minButtonWidth:75,
26911
26912     /**
26913      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26914      * This property cascades to child containers if not set.
26915      */
26916     labelAlign:'left',
26917
26918     /**
26919      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26920      * fires a looping event with that state. This is required to bind buttons to the valid
26921      * state using the config value formBind:true on the button.
26922      */
26923     monitorValid : false,
26924
26925     /**
26926      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26927      */
26928     monitorPoll : 200,
26929     
26930     /**
26931      * @cfg {String} progressUrl - Url to return progress data 
26932      */
26933     
26934     progressUrl : false,
26935     /**
26936      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
26937      * sending a formdata with extra parameters - eg uploaded elements.
26938      */
26939     
26940     formData : false,
26941     
26942     /**
26943      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26944      * fields are added and the column is closed. If no fields are passed the column remains open
26945      * until end() is called.
26946      * @param {Object} config The config to pass to the column
26947      * @param {Field} field1 (optional)
26948      * @param {Field} field2 (optional)
26949      * @param {Field} etc (optional)
26950      * @return Column The column container object
26951      */
26952     column : function(c){
26953         var col = new Roo.form.Column(c);
26954         this.start(col);
26955         if(arguments.length > 1){ // duplicate code required because of Opera
26956             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26957             this.end();
26958         }
26959         return col;
26960     },
26961
26962     /**
26963      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26964      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26965      * until end() is called.
26966      * @param {Object} config The config to pass to the fieldset
26967      * @param {Field} field1 (optional)
26968      * @param {Field} field2 (optional)
26969      * @param {Field} etc (optional)
26970      * @return FieldSet The fieldset container object
26971      */
26972     fieldset : function(c){
26973         var fs = new Roo.form.FieldSet(c);
26974         this.start(fs);
26975         if(arguments.length > 1){ // duplicate code required because of Opera
26976             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26977             this.end();
26978         }
26979         return fs;
26980     },
26981
26982     /**
26983      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26984      * fields are added and the container is closed. If no fields are passed the container remains open
26985      * until end() is called.
26986      * @param {Object} config The config to pass to the Layout
26987      * @param {Field} field1 (optional)
26988      * @param {Field} field2 (optional)
26989      * @param {Field} etc (optional)
26990      * @return Layout The container object
26991      */
26992     container : function(c){
26993         var l = new Roo.form.Layout(c);
26994         this.start(l);
26995         if(arguments.length > 1){ // duplicate code required because of Opera
26996             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26997             this.end();
26998         }
26999         return l;
27000     },
27001
27002     /**
27003      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
27004      * @param {Object} container A Roo.form.Layout or subclass of Layout
27005      * @return {Form} this
27006      */
27007     start : function(c){
27008         // cascade label info
27009         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
27010         this.active.stack.push(c);
27011         c.ownerCt = this.active;
27012         this.active = c;
27013         return this;
27014     },
27015
27016     /**
27017      * Closes the current open container
27018      * @return {Form} this
27019      */
27020     end : function(){
27021         if(this.active == this.root){
27022             return this;
27023         }
27024         this.active = this.active.ownerCt;
27025         return this;
27026     },
27027
27028     /**
27029      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
27030      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
27031      * as the label of the field.
27032      * @param {Field} field1
27033      * @param {Field} field2 (optional)
27034      * @param {Field} etc. (optional)
27035      * @return {Form} this
27036      */
27037     add : function(){
27038         this.active.stack.push.apply(this.active.stack, arguments);
27039         this.allItems.push.apply(this.allItems,arguments);
27040         var r = [];
27041         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
27042             if(a[i].isFormField){
27043                 r.push(a[i]);
27044             }
27045         }
27046         if(r.length > 0){
27047             Roo.form.Form.superclass.add.apply(this, r);
27048         }
27049         return this;
27050     },
27051     
27052
27053     
27054     
27055     
27056      /**
27057      * Find any element that has been added to a form, using it's ID or name
27058      * This can include framesets, columns etc. along with regular fields..
27059      * @param {String} id - id or name to find.
27060      
27061      * @return {Element} e - or false if nothing found.
27062      */
27063     findbyId : function(id)
27064     {
27065         var ret = false;
27066         if (!id) {
27067             return ret;
27068         }
27069         Roo.each(this.allItems, function(f){
27070             if (f.id == id || f.name == id ){
27071                 ret = f;
27072                 return false;
27073             }
27074         });
27075         return ret;
27076     },
27077
27078     
27079     
27080     /**
27081      * Render this form into the passed container. This should only be called once!
27082      * @param {String/HTMLElement/Element} container The element this component should be rendered into
27083      * @return {Form} this
27084      */
27085     render : function(ct)
27086     {
27087         
27088         
27089         
27090         ct = Roo.get(ct);
27091         var o = this.autoCreate || {
27092             tag: 'form',
27093             method : this.method || 'POST',
27094             id : this.id || Roo.id()
27095         };
27096         this.initEl(ct.createChild(o));
27097
27098         this.root.render(this.el);
27099         
27100        
27101              
27102         this.items.each(function(f){
27103             f.render('x-form-el-'+f.id);
27104         });
27105
27106         if(this.buttons.length > 0){
27107             // tables are required to maintain order and for correct IE layout
27108             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
27109                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
27110                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
27111             }}, null, true);
27112             var tr = tb.getElementsByTagName('tr')[0];
27113             for(var i = 0, len = this.buttons.length; i < len; i++) {
27114                 var b = this.buttons[i];
27115                 var td = document.createElement('td');
27116                 td.className = 'x-form-btn-td';
27117                 b.render(tr.appendChild(td));
27118             }
27119         }
27120         if(this.monitorValid){ // initialize after render
27121             this.startMonitoring();
27122         }
27123         this.fireEvent('rendered', this);
27124         return this;
27125     },
27126
27127     /**
27128      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
27129      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
27130      * object or a valid Roo.DomHelper element config
27131      * @param {Function} handler The function called when the button is clicked
27132      * @param {Object} scope (optional) The scope of the handler function
27133      * @return {Roo.Button}
27134      */
27135     addButton : function(config, handler, scope){
27136         var bc = {
27137             handler: handler,
27138             scope: scope,
27139             minWidth: this.minButtonWidth,
27140             hideParent:true
27141         };
27142         if(typeof config == "string"){
27143             bc.text = config;
27144         }else{
27145             Roo.apply(bc, config);
27146         }
27147         var btn = new Roo.Button(null, bc);
27148         this.buttons.push(btn);
27149         return btn;
27150     },
27151
27152      /**
27153      * Adds a series of form elements (using the xtype property as the factory method.
27154      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
27155      * @param {Object} config 
27156      */
27157     
27158     addxtype : function()
27159     {
27160         var ar = Array.prototype.slice.call(arguments, 0);
27161         var ret = false;
27162         for(var i = 0; i < ar.length; i++) {
27163             if (!ar[i]) {
27164                 continue; // skip -- if this happends something invalid got sent, we 
27165                 // should ignore it, as basically that interface element will not show up
27166                 // and that should be pretty obvious!!
27167             }
27168             
27169             if (Roo.form[ar[i].xtype]) {
27170                 ar[i].form = this;
27171                 var fe = Roo.factory(ar[i], Roo.form);
27172                 if (!ret) {
27173                     ret = fe;
27174                 }
27175                 fe.form = this;
27176                 if (fe.store) {
27177                     fe.store.form = this;
27178                 }
27179                 if (fe.isLayout) {  
27180                          
27181                     this.start(fe);
27182                     this.allItems.push(fe);
27183                     if (fe.items && fe.addxtype) {
27184                         fe.addxtype.apply(fe, fe.items);
27185                         delete fe.items;
27186                     }
27187                      this.end();
27188                     continue;
27189                 }
27190                 
27191                 
27192                  
27193                 this.add(fe);
27194               //  console.log('adding ' + ar[i].xtype);
27195             }
27196             if (ar[i].xtype == 'Button') {  
27197                 //console.log('adding button');
27198                 //console.log(ar[i]);
27199                 this.addButton(ar[i]);
27200                 this.allItems.push(fe);
27201                 continue;
27202             }
27203             
27204             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
27205                 alert('end is not supported on xtype any more, use items');
27206             //    this.end();
27207             //    //console.log('adding end');
27208             }
27209             
27210         }
27211         return ret;
27212     },
27213     
27214     /**
27215      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
27216      * option "monitorValid"
27217      */
27218     startMonitoring : function(){
27219         if(!this.bound){
27220             this.bound = true;
27221             Roo.TaskMgr.start({
27222                 run : this.bindHandler,
27223                 interval : this.monitorPoll || 200,
27224                 scope: this
27225             });
27226         }
27227     },
27228
27229     /**
27230      * Stops monitoring of the valid state of this form
27231      */
27232     stopMonitoring : function(){
27233         this.bound = false;
27234     },
27235
27236     // private
27237     bindHandler : function(){
27238         if(!this.bound){
27239             return false; // stops binding
27240         }
27241         var valid = true;
27242         this.items.each(function(f){
27243             if(!f.isValid(true)){
27244                 valid = false;
27245                 return false;
27246             }
27247         });
27248         for(var i = 0, len = this.buttons.length; i < len; i++){
27249             var btn = this.buttons[i];
27250             if(btn.formBind === true && btn.disabled === valid){
27251                 btn.setDisabled(!valid);
27252             }
27253         }
27254         this.fireEvent('clientvalidation', this, valid);
27255     }
27256     
27257     
27258     
27259     
27260     
27261     
27262     
27263     
27264 });
27265
27266
27267 // back compat
27268 Roo.Form = Roo.form.Form;
27269 /*
27270  * Based on:
27271  * Ext JS Library 1.1.1
27272  * Copyright(c) 2006-2007, Ext JS, LLC.
27273  *
27274  * Originally Released Under LGPL - original licence link has changed is not relivant.
27275  *
27276  * Fork - LGPL
27277  * <script type="text/javascript">
27278  */
27279
27280 // as we use this in bootstrap.
27281 Roo.namespace('Roo.form');
27282  /**
27283  * @class Roo.form.Action
27284  * Internal Class used to handle form actions
27285  * @constructor
27286  * @param {Roo.form.BasicForm} el The form element or its id
27287  * @param {Object} config Configuration options
27288  */
27289
27290  
27291  
27292 // define the action interface
27293 Roo.form.Action = function(form, options){
27294     this.form = form;
27295     this.options = options || {};
27296 };
27297 /**
27298  * Client Validation Failed
27299  * @const 
27300  */
27301 Roo.form.Action.CLIENT_INVALID = 'client';
27302 /**
27303  * Server Validation Failed
27304  * @const 
27305  */
27306 Roo.form.Action.SERVER_INVALID = 'server';
27307  /**
27308  * Connect to Server Failed
27309  * @const 
27310  */
27311 Roo.form.Action.CONNECT_FAILURE = 'connect';
27312 /**
27313  * Reading Data from Server Failed
27314  * @const 
27315  */
27316 Roo.form.Action.LOAD_FAILURE = 'load';
27317
27318 Roo.form.Action.prototype = {
27319     type : 'default',
27320     failureType : undefined,
27321     response : undefined,
27322     result : undefined,
27323
27324     // interface method
27325     run : function(options){
27326
27327     },
27328
27329     // interface method
27330     success : function(response){
27331
27332     },
27333
27334     // interface method
27335     handleResponse : function(response){
27336
27337     },
27338
27339     // default connection failure
27340     failure : function(response){
27341         
27342         this.response = response;
27343         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27344         this.form.afterAction(this, false);
27345     },
27346
27347     processResponse : function(response){
27348         this.response = response;
27349         if(!response.responseText){
27350             return true;
27351         }
27352         this.result = this.handleResponse(response);
27353         return this.result;
27354     },
27355
27356     // utility functions used internally
27357     getUrl : function(appendParams){
27358         var url = this.options.url || this.form.url || this.form.el.dom.action;
27359         if(appendParams){
27360             var p = this.getParams();
27361             if(p){
27362                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
27363             }
27364         }
27365         return url;
27366     },
27367
27368     getMethod : function(){
27369         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
27370     },
27371
27372     getParams : function(){
27373         var bp = this.form.baseParams;
27374         var p = this.options.params;
27375         if(p){
27376             if(typeof p == "object"){
27377                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27378             }else if(typeof p == 'string' && bp){
27379                 p += '&' + Roo.urlEncode(bp);
27380             }
27381         }else if(bp){
27382             p = Roo.urlEncode(bp);
27383         }
27384         return p;
27385     },
27386
27387     createCallback : function(){
27388         return {
27389             success: this.success,
27390             failure: this.failure,
27391             scope: this,
27392             timeout: (this.form.timeout*1000),
27393             upload: this.form.fileUpload ? this.success : undefined
27394         };
27395     }
27396 };
27397
27398 Roo.form.Action.Submit = function(form, options){
27399     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27400 };
27401
27402 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27403     type : 'submit',
27404
27405     haveProgress : false,
27406     uploadComplete : false,
27407     
27408     // uploadProgress indicator.
27409     uploadProgress : function()
27410     {
27411         if (!this.form.progressUrl) {
27412             return;
27413         }
27414         
27415         if (!this.haveProgress) {
27416             Roo.MessageBox.progress("Uploading", "Uploading");
27417         }
27418         if (this.uploadComplete) {
27419            Roo.MessageBox.hide();
27420            return;
27421         }
27422         
27423         this.haveProgress = true;
27424    
27425         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27426         
27427         var c = new Roo.data.Connection();
27428         c.request({
27429             url : this.form.progressUrl,
27430             params: {
27431                 id : uid
27432             },
27433             method: 'GET',
27434             success : function(req){
27435                //console.log(data);
27436                 var rdata = false;
27437                 var edata;
27438                 try  {
27439                    rdata = Roo.decode(req.responseText)
27440                 } catch (e) {
27441                     Roo.log("Invalid data from server..");
27442                     Roo.log(edata);
27443                     return;
27444                 }
27445                 if (!rdata || !rdata.success) {
27446                     Roo.log(rdata);
27447                     Roo.MessageBox.alert(Roo.encode(rdata));
27448                     return;
27449                 }
27450                 var data = rdata.data;
27451                 
27452                 if (this.uploadComplete) {
27453                    Roo.MessageBox.hide();
27454                    return;
27455                 }
27456                    
27457                 if (data){
27458                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27459                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27460                     );
27461                 }
27462                 this.uploadProgress.defer(2000,this);
27463             },
27464        
27465             failure: function(data) {
27466                 Roo.log('progress url failed ');
27467                 Roo.log(data);
27468             },
27469             scope : this
27470         });
27471            
27472     },
27473     
27474     
27475     run : function()
27476     {
27477         // run get Values on the form, so it syncs any secondary forms.
27478         this.form.getValues();
27479         
27480         var o = this.options;
27481         var method = this.getMethod();
27482         var isPost = method == 'POST';
27483         if(o.clientValidation === false || this.form.isValid()){
27484             
27485             if (this.form.progressUrl) {
27486                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27487                     (new Date() * 1) + '' + Math.random());
27488                     
27489             } 
27490             
27491             
27492             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27493                 form:this.form.el.dom,
27494                 url:this.getUrl(!isPost),
27495                 method: method,
27496                 params:isPost ? this.getParams() : null,
27497                 isUpload: this.form.fileUpload,
27498                 formData : this.form.formData
27499             }));
27500             
27501             this.uploadProgress();
27502
27503         }else if (o.clientValidation !== false){ // client validation failed
27504             this.failureType = Roo.form.Action.CLIENT_INVALID;
27505             this.form.afterAction(this, false);
27506         }
27507     },
27508
27509     success : function(response)
27510     {
27511         this.uploadComplete= true;
27512         if (this.haveProgress) {
27513             Roo.MessageBox.hide();
27514         }
27515         
27516         
27517         var result = this.processResponse(response);
27518         if(result === true || result.success){
27519             this.form.afterAction(this, true);
27520             return;
27521         }
27522         if(result.errors){
27523             this.form.markInvalid(result.errors);
27524             this.failureType = Roo.form.Action.SERVER_INVALID;
27525         }
27526         this.form.afterAction(this, false);
27527     },
27528     failure : function(response)
27529     {
27530         this.uploadComplete= true;
27531         if (this.haveProgress) {
27532             Roo.MessageBox.hide();
27533         }
27534         
27535         this.response = response;
27536         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27537         this.form.afterAction(this, false);
27538     },
27539     
27540     handleResponse : function(response){
27541         if(this.form.errorReader){
27542             var rs = this.form.errorReader.read(response);
27543             var errors = [];
27544             if(rs.records){
27545                 for(var i = 0, len = rs.records.length; i < len; i++) {
27546                     var r = rs.records[i];
27547                     errors[i] = r.data;
27548                 }
27549             }
27550             if(errors.length < 1){
27551                 errors = null;
27552             }
27553             return {
27554                 success : rs.success,
27555                 errors : errors
27556             };
27557         }
27558         var ret = false;
27559         try {
27560             ret = Roo.decode(response.responseText);
27561         } catch (e) {
27562             ret = {
27563                 success: false,
27564                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27565                 errors : []
27566             };
27567         }
27568         return ret;
27569         
27570     }
27571 });
27572
27573
27574 Roo.form.Action.Load = function(form, options){
27575     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27576     this.reader = this.form.reader;
27577 };
27578
27579 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27580     type : 'load',
27581
27582     run : function(){
27583         
27584         Roo.Ajax.request(Roo.apply(
27585                 this.createCallback(), {
27586                     method:this.getMethod(),
27587                     url:this.getUrl(false),
27588                     params:this.getParams()
27589         }));
27590     },
27591
27592     success : function(response){
27593         
27594         var result = this.processResponse(response);
27595         if(result === true || !result.success || !result.data){
27596             this.failureType = Roo.form.Action.LOAD_FAILURE;
27597             this.form.afterAction(this, false);
27598             return;
27599         }
27600         this.form.clearInvalid();
27601         this.form.setValues(result.data);
27602         this.form.afterAction(this, true);
27603     },
27604
27605     handleResponse : function(response){
27606         if(this.form.reader){
27607             var rs = this.form.reader.read(response);
27608             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27609             return {
27610                 success : rs.success,
27611                 data : data
27612             };
27613         }
27614         return Roo.decode(response.responseText);
27615     }
27616 });
27617
27618 Roo.form.Action.ACTION_TYPES = {
27619     'load' : Roo.form.Action.Load,
27620     'submit' : Roo.form.Action.Submit
27621 };/*
27622  * Based on:
27623  * Ext JS Library 1.1.1
27624  * Copyright(c) 2006-2007, Ext JS, LLC.
27625  *
27626  * Originally Released Under LGPL - original licence link has changed is not relivant.
27627  *
27628  * Fork - LGPL
27629  * <script type="text/javascript">
27630  */
27631  
27632 /**
27633  * @class Roo.form.Layout
27634  * @extends Roo.Component
27635  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
27636  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27637  * @constructor
27638  * @param {Object} config Configuration options
27639  */
27640 Roo.form.Layout = function(config){
27641     var xitems = [];
27642     if (config.items) {
27643         xitems = config.items;
27644         delete config.items;
27645     }
27646     Roo.form.Layout.superclass.constructor.call(this, config);
27647     this.stack = [];
27648     Roo.each(xitems, this.addxtype, this);
27649      
27650 };
27651
27652 Roo.extend(Roo.form.Layout, Roo.Component, {
27653     /**
27654      * @cfg {String/Object} autoCreate
27655      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
27656      */
27657     /**
27658      * @cfg {String/Object/Function} style
27659      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
27660      * a function which returns such a specification.
27661      */
27662     /**
27663      * @cfg {String} labelAlign
27664      * Valid values are "left," "top" and "right" (defaults to "left")
27665      */
27666     /**
27667      * @cfg {Number} labelWidth
27668      * Fixed width in pixels of all field labels (defaults to undefined)
27669      */
27670     /**
27671      * @cfg {Boolean} clear
27672      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
27673      */
27674     clear : true,
27675     /**
27676      * @cfg {String} labelSeparator
27677      * The separator to use after field labels (defaults to ':')
27678      */
27679     labelSeparator : ':',
27680     /**
27681      * @cfg {Boolean} hideLabels
27682      * True to suppress the display of field labels in this layout (defaults to false)
27683      */
27684     hideLabels : false,
27685
27686     // private
27687     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
27688     
27689     isLayout : true,
27690     
27691     // private
27692     onRender : function(ct, position){
27693         if(this.el){ // from markup
27694             this.el = Roo.get(this.el);
27695         }else {  // generate
27696             var cfg = this.getAutoCreate();
27697             this.el = ct.createChild(cfg, position);
27698         }
27699         if(this.style){
27700             this.el.applyStyles(this.style);
27701         }
27702         if(this.labelAlign){
27703             this.el.addClass('x-form-label-'+this.labelAlign);
27704         }
27705         if(this.hideLabels){
27706             this.labelStyle = "display:none";
27707             this.elementStyle = "padding-left:0;";
27708         }else{
27709             if(typeof this.labelWidth == 'number'){
27710                 this.labelStyle = "width:"+this.labelWidth+"px;";
27711                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
27712             }
27713             if(this.labelAlign == 'top'){
27714                 this.labelStyle = "width:auto;";
27715                 this.elementStyle = "padding-left:0;";
27716             }
27717         }
27718         var stack = this.stack;
27719         var slen = stack.length;
27720         if(slen > 0){
27721             if(!this.fieldTpl){
27722                 var t = new Roo.Template(
27723                     '<div class="x-form-item {5}">',
27724                         '<label for="{0}" style="{2}">{1}{4}</label>',
27725                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27726                         '</div>',
27727                     '</div><div class="x-form-clear-left"></div>'
27728                 );
27729                 t.disableFormats = true;
27730                 t.compile();
27731                 Roo.form.Layout.prototype.fieldTpl = t;
27732             }
27733             for(var i = 0; i < slen; i++) {
27734                 if(stack[i].isFormField){
27735                     this.renderField(stack[i]);
27736                 }else{
27737                     this.renderComponent(stack[i]);
27738                 }
27739             }
27740         }
27741         if(this.clear){
27742             this.el.createChild({cls:'x-form-clear'});
27743         }
27744     },
27745
27746     // private
27747     renderField : function(f){
27748         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
27749                f.id, //0
27750                f.fieldLabel, //1
27751                f.labelStyle||this.labelStyle||'', //2
27752                this.elementStyle||'', //3
27753                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
27754                f.itemCls||this.itemCls||''  //5
27755        ], true).getPrevSibling());
27756     },
27757
27758     // private
27759     renderComponent : function(c){
27760         c.render(c.isLayout ? this.el : this.el.createChild());    
27761     },
27762     /**
27763      * Adds a object form elements (using the xtype property as the factory method.)
27764      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
27765      * @param {Object} config 
27766      */
27767     addxtype : function(o)
27768     {
27769         // create the lement.
27770         o.form = this.form;
27771         var fe = Roo.factory(o, Roo.form);
27772         this.form.allItems.push(fe);
27773         this.stack.push(fe);
27774         
27775         if (fe.isFormField) {
27776             this.form.items.add(fe);
27777         }
27778          
27779         return fe;
27780     }
27781 });
27782
27783 /**
27784  * @class Roo.form.Column
27785  * @extends Roo.form.Layout
27786  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
27787  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
27788  * @constructor
27789  * @param {Object} config Configuration options
27790  */
27791 Roo.form.Column = function(config){
27792     Roo.form.Column.superclass.constructor.call(this, config);
27793 };
27794
27795 Roo.extend(Roo.form.Column, Roo.form.Layout, {
27796     /**
27797      * @cfg {Number/String} width
27798      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27799      */
27800     /**
27801      * @cfg {String/Object} autoCreate
27802      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
27803      */
27804
27805     // private
27806     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
27807
27808     // private
27809     onRender : function(ct, position){
27810         Roo.form.Column.superclass.onRender.call(this, ct, position);
27811         if(this.width){
27812             this.el.setWidth(this.width);
27813         }
27814     }
27815 });
27816
27817
27818 /**
27819  * @class Roo.form.Row
27820  * @extends Roo.form.Layout
27821  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
27822  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
27823  * @constructor
27824  * @param {Object} config Configuration options
27825  */
27826
27827  
27828 Roo.form.Row = function(config){
27829     Roo.form.Row.superclass.constructor.call(this, config);
27830 };
27831  
27832 Roo.extend(Roo.form.Row, Roo.form.Layout, {
27833       /**
27834      * @cfg {Number/String} width
27835      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27836      */
27837     /**
27838      * @cfg {Number/String} height
27839      * The fixed height of the column in pixels or CSS value (defaults to "auto")
27840      */
27841     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
27842     
27843     padWidth : 20,
27844     // private
27845     onRender : function(ct, position){
27846         //console.log('row render');
27847         if(!this.rowTpl){
27848             var t = new Roo.Template(
27849                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
27850                     '<label for="{0}" style="{2}">{1}{4}</label>',
27851                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27852                     '</div>',
27853                 '</div>'
27854             );
27855             t.disableFormats = true;
27856             t.compile();
27857             Roo.form.Layout.prototype.rowTpl = t;
27858         }
27859         this.fieldTpl = this.rowTpl;
27860         
27861         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
27862         var labelWidth = 100;
27863         
27864         if ((this.labelAlign != 'top')) {
27865             if (typeof this.labelWidth == 'number') {
27866                 labelWidth = this.labelWidth
27867             }
27868             this.padWidth =  20 + labelWidth;
27869             
27870         }
27871         
27872         Roo.form.Column.superclass.onRender.call(this, ct, position);
27873         if(this.width){
27874             this.el.setWidth(this.width);
27875         }
27876         if(this.height){
27877             this.el.setHeight(this.height);
27878         }
27879     },
27880     
27881     // private
27882     renderField : function(f){
27883         f.fieldEl = this.fieldTpl.append(this.el, [
27884                f.id, f.fieldLabel,
27885                f.labelStyle||this.labelStyle||'',
27886                this.elementStyle||'',
27887                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
27888                f.itemCls||this.itemCls||'',
27889                f.width ? f.width + this.padWidth : 160 + this.padWidth
27890        ],true);
27891     }
27892 });
27893  
27894
27895 /**
27896  * @class Roo.form.FieldSet
27897  * @extends Roo.form.Layout
27898  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
27899  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
27900  * @constructor
27901  * @param {Object} config Configuration options
27902  */
27903 Roo.form.FieldSet = function(config){
27904     Roo.form.FieldSet.superclass.constructor.call(this, config);
27905 };
27906
27907 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
27908     /**
27909      * @cfg {String} legend
27910      * The text to display as the legend for the FieldSet (defaults to '')
27911      */
27912     /**
27913      * @cfg {String/Object} autoCreate
27914      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
27915      */
27916
27917     // private
27918     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
27919
27920     // private
27921     onRender : function(ct, position){
27922         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
27923         if(this.legend){
27924             this.setLegend(this.legend);
27925         }
27926     },
27927
27928     // private
27929     setLegend : function(text){
27930         if(this.rendered){
27931             this.el.child('legend').update(text);
27932         }
27933     }
27934 });/*
27935  * Based on:
27936  * Ext JS Library 1.1.1
27937  * Copyright(c) 2006-2007, Ext JS, LLC.
27938  *
27939  * Originally Released Under LGPL - original licence link has changed is not relivant.
27940  *
27941  * Fork - LGPL
27942  * <script type="text/javascript">
27943  */
27944 /**
27945  * @class Roo.form.VTypes
27946  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
27947  * @static
27948  */
27949 Roo.form.VTypes = function(){
27950     // closure these in so they are only created once.
27951     var alpha = /^[a-zA-Z_]+$/;
27952     var alphanum = /^[a-zA-Z0-9_]+$/;
27953     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
27954     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
27955
27956     // All these messages and functions are configurable
27957     return {
27958         /**
27959          * The function used to validate email addresses
27960          * @param {String} value The email address
27961          */
27962         'email' : function(v){
27963             return email.test(v);
27964         },
27965         /**
27966          * The error text to display when the email validation function returns false
27967          * @type String
27968          */
27969         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
27970         /**
27971          * The keystroke filter mask to be applied on email input
27972          * @type RegExp
27973          */
27974         'emailMask' : /[a-z0-9_\.\-@]/i,
27975
27976         /**
27977          * The function used to validate URLs
27978          * @param {String} value The URL
27979          */
27980         'url' : function(v){
27981             return url.test(v);
27982         },
27983         /**
27984          * The error text to display when the url validation function returns false
27985          * @type String
27986          */
27987         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
27988         
27989         /**
27990          * The function used to validate alpha values
27991          * @param {String} value The value
27992          */
27993         'alpha' : function(v){
27994             return alpha.test(v);
27995         },
27996         /**
27997          * The error text to display when the alpha validation function returns false
27998          * @type String
27999          */
28000         'alphaText' : 'This field should only contain letters and _',
28001         /**
28002          * The keystroke filter mask to be applied on alpha input
28003          * @type RegExp
28004          */
28005         'alphaMask' : /[a-z_]/i,
28006
28007         /**
28008          * The function used to validate alphanumeric values
28009          * @param {String} value The value
28010          */
28011         'alphanum' : function(v){
28012             return alphanum.test(v);
28013         },
28014         /**
28015          * The error text to display when the alphanumeric validation function returns false
28016          * @type String
28017          */
28018         'alphanumText' : 'This field should only contain letters, numbers and _',
28019         /**
28020          * The keystroke filter mask to be applied on alphanumeric input
28021          * @type RegExp
28022          */
28023         'alphanumMask' : /[a-z0-9_]/i
28024     };
28025 }();//<script type="text/javascript">
28026
28027 /**
28028  * @class Roo.form.FCKeditor
28029  * @extends Roo.form.TextArea
28030  * Wrapper around the FCKEditor http://www.fckeditor.net
28031  * @constructor
28032  * Creates a new FCKeditor
28033  * @param {Object} config Configuration options
28034  */
28035 Roo.form.FCKeditor = function(config){
28036     Roo.form.FCKeditor.superclass.constructor.call(this, config);
28037     this.addEvents({
28038          /**
28039          * @event editorinit
28040          * Fired when the editor is initialized - you can add extra handlers here..
28041          * @param {FCKeditor} this
28042          * @param {Object} the FCK object.
28043          */
28044         editorinit : true
28045     });
28046     
28047     
28048 };
28049 Roo.form.FCKeditor.editors = { };
28050 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
28051 {
28052     //defaultAutoCreate : {
28053     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
28054     //},
28055     // private
28056     /**
28057      * @cfg {Object} fck options - see fck manual for details.
28058      */
28059     fckconfig : false,
28060     
28061     /**
28062      * @cfg {Object} fck toolbar set (Basic or Default)
28063      */
28064     toolbarSet : 'Basic',
28065     /**
28066      * @cfg {Object} fck BasePath
28067      */ 
28068     basePath : '/fckeditor/',
28069     
28070     
28071     frame : false,
28072     
28073     value : '',
28074     
28075    
28076     onRender : function(ct, position)
28077     {
28078         if(!this.el){
28079             this.defaultAutoCreate = {
28080                 tag: "textarea",
28081                 style:"width:300px;height:60px;",
28082                 autocomplete: "new-password"
28083             };
28084         }
28085         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
28086         /*
28087         if(this.grow){
28088             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
28089             if(this.preventScrollbars){
28090                 this.el.setStyle("overflow", "hidden");
28091             }
28092             this.el.setHeight(this.growMin);
28093         }
28094         */
28095         //console.log('onrender' + this.getId() );
28096         Roo.form.FCKeditor.editors[this.getId()] = this;
28097          
28098
28099         this.replaceTextarea() ;
28100         
28101     },
28102     
28103     getEditor : function() {
28104         return this.fckEditor;
28105     },
28106     /**
28107      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
28108      * @param {Mixed} value The value to set
28109      */
28110     
28111     
28112     setValue : function(value)
28113     {
28114         //console.log('setValue: ' + value);
28115         
28116         if(typeof(value) == 'undefined') { // not sure why this is happending...
28117             return;
28118         }
28119         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28120         
28121         //if(!this.el || !this.getEditor()) {
28122         //    this.value = value;
28123             //this.setValue.defer(100,this,[value]);    
28124         //    return;
28125         //} 
28126         
28127         if(!this.getEditor()) {
28128             return;
28129         }
28130         
28131         this.getEditor().SetData(value);
28132         
28133         //
28134
28135     },
28136
28137     /**
28138      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
28139      * @return {Mixed} value The field value
28140      */
28141     getValue : function()
28142     {
28143         
28144         if (this.frame && this.frame.dom.style.display == 'none') {
28145             return Roo.form.FCKeditor.superclass.getValue.call(this);
28146         }
28147         
28148         if(!this.el || !this.getEditor()) {
28149            
28150            // this.getValue.defer(100,this); 
28151             return this.value;
28152         }
28153        
28154         
28155         var value=this.getEditor().GetData();
28156         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28157         return Roo.form.FCKeditor.superclass.getValue.call(this);
28158         
28159
28160     },
28161
28162     /**
28163      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
28164      * @return {Mixed} value The field value
28165      */
28166     getRawValue : function()
28167     {
28168         if (this.frame && this.frame.dom.style.display == 'none') {
28169             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28170         }
28171         
28172         if(!this.el || !this.getEditor()) {
28173             //this.getRawValue.defer(100,this); 
28174             return this.value;
28175             return;
28176         }
28177         
28178         
28179         
28180         var value=this.getEditor().GetData();
28181         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
28182         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28183          
28184     },
28185     
28186     setSize : function(w,h) {
28187         
28188         
28189         
28190         //if (this.frame && this.frame.dom.style.display == 'none') {
28191         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28192         //    return;
28193         //}
28194         //if(!this.el || !this.getEditor()) {
28195         //    this.setSize.defer(100,this, [w,h]); 
28196         //    return;
28197         //}
28198         
28199         
28200         
28201         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28202         
28203         this.frame.dom.setAttribute('width', w);
28204         this.frame.dom.setAttribute('height', h);
28205         this.frame.setSize(w,h);
28206         
28207     },
28208     
28209     toggleSourceEdit : function(value) {
28210         
28211       
28212          
28213         this.el.dom.style.display = value ? '' : 'none';
28214         this.frame.dom.style.display = value ?  'none' : '';
28215         
28216     },
28217     
28218     
28219     focus: function(tag)
28220     {
28221         if (this.frame.dom.style.display == 'none') {
28222             return Roo.form.FCKeditor.superclass.focus.call(this);
28223         }
28224         if(!this.el || !this.getEditor()) {
28225             this.focus.defer(100,this, [tag]); 
28226             return;
28227         }
28228         
28229         
28230         
28231         
28232         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
28233         this.getEditor().Focus();
28234         if (tgs.length) {
28235             if (!this.getEditor().Selection.GetSelection()) {
28236                 this.focus.defer(100,this, [tag]); 
28237                 return;
28238             }
28239             
28240             
28241             var r = this.getEditor().EditorDocument.createRange();
28242             r.setStart(tgs[0],0);
28243             r.setEnd(tgs[0],0);
28244             this.getEditor().Selection.GetSelection().removeAllRanges();
28245             this.getEditor().Selection.GetSelection().addRange(r);
28246             this.getEditor().Focus();
28247         }
28248         
28249     },
28250     
28251     
28252     
28253     replaceTextarea : function()
28254     {
28255         if ( document.getElementById( this.getId() + '___Frame' ) ) {
28256             return ;
28257         }
28258         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
28259         //{
28260             // We must check the elements firstly using the Id and then the name.
28261         var oTextarea = document.getElementById( this.getId() );
28262         
28263         var colElementsByName = document.getElementsByName( this.getId() ) ;
28264          
28265         oTextarea.style.display = 'none' ;
28266
28267         if ( oTextarea.tabIndex ) {            
28268             this.TabIndex = oTextarea.tabIndex ;
28269         }
28270         
28271         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
28272         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
28273         this.frame = Roo.get(this.getId() + '___Frame')
28274     },
28275     
28276     _getConfigHtml : function()
28277     {
28278         var sConfig = '' ;
28279
28280         for ( var o in this.fckconfig ) {
28281             sConfig += sConfig.length > 0  ? '&amp;' : '';
28282             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
28283         }
28284
28285         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
28286     },
28287     
28288     
28289     _getIFrameHtml : function()
28290     {
28291         var sFile = 'fckeditor.html' ;
28292         /* no idea what this is about..
28293         try
28294         {
28295             if ( (/fcksource=true/i).test( window.top.location.search ) )
28296                 sFile = 'fckeditor.original.html' ;
28297         }
28298         catch (e) { 
28299         */
28300
28301         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
28302         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
28303         
28304         
28305         var html = '<iframe id="' + this.getId() +
28306             '___Frame" src="' + sLink +
28307             '" width="' + this.width +
28308             '" height="' + this.height + '"' +
28309             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
28310             ' frameborder="0" scrolling="no"></iframe>' ;
28311
28312         return html ;
28313     },
28314     
28315     _insertHtmlBefore : function( html, element )
28316     {
28317         if ( element.insertAdjacentHTML )       {
28318             // IE
28319             element.insertAdjacentHTML( 'beforeBegin', html ) ;
28320         } else { // Gecko
28321             var oRange = document.createRange() ;
28322             oRange.setStartBefore( element ) ;
28323             var oFragment = oRange.createContextualFragment( html );
28324             element.parentNode.insertBefore( oFragment, element ) ;
28325         }
28326     }
28327     
28328     
28329   
28330     
28331     
28332     
28333     
28334
28335 });
28336
28337 //Roo.reg('fckeditor', Roo.form.FCKeditor);
28338
28339 function FCKeditor_OnComplete(editorInstance){
28340     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
28341     f.fckEditor = editorInstance;
28342     //console.log("loaded");
28343     f.fireEvent('editorinit', f, editorInstance);
28344
28345   
28346
28347  
28348
28349
28350
28351
28352
28353
28354
28355
28356
28357
28358
28359
28360
28361
28362
28363 //<script type="text/javascript">
28364 /**
28365  * @class Roo.form.GridField
28366  * @extends Roo.form.Field
28367  * Embed a grid (or editable grid into a form)
28368  * STATUS ALPHA
28369  * 
28370  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
28371  * it needs 
28372  * xgrid.store = Roo.data.Store
28373  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
28374  * xgrid.store.reader = Roo.data.JsonReader 
28375  * 
28376  * 
28377  * @constructor
28378  * Creates a new GridField
28379  * @param {Object} config Configuration options
28380  */
28381 Roo.form.GridField = function(config){
28382     Roo.form.GridField.superclass.constructor.call(this, config);
28383      
28384 };
28385
28386 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
28387     /**
28388      * @cfg {Number} width  - used to restrict width of grid..
28389      */
28390     width : 100,
28391     /**
28392      * @cfg {Number} height - used to restrict height of grid..
28393      */
28394     height : 50,
28395      /**
28396      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28397          * 
28398          *}
28399      */
28400     xgrid : false, 
28401     /**
28402      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28403      * {tag: "input", type: "checkbox", autocomplete: "off"})
28404      */
28405    // defaultAutoCreate : { tag: 'div' },
28406     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
28407     /**
28408      * @cfg {String} addTitle Text to include for adding a title.
28409      */
28410     addTitle : false,
28411     //
28412     onResize : function(){
28413         Roo.form.Field.superclass.onResize.apply(this, arguments);
28414     },
28415
28416     initEvents : function(){
28417         // Roo.form.Checkbox.superclass.initEvents.call(this);
28418         // has no events...
28419        
28420     },
28421
28422
28423     getResizeEl : function(){
28424         return this.wrap;
28425     },
28426
28427     getPositionEl : function(){
28428         return this.wrap;
28429     },
28430
28431     // private
28432     onRender : function(ct, position){
28433         
28434         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28435         var style = this.style;
28436         delete this.style;
28437         
28438         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28439         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28440         this.viewEl = this.wrap.createChild({ tag: 'div' });
28441         if (style) {
28442             this.viewEl.applyStyles(style);
28443         }
28444         if (this.width) {
28445             this.viewEl.setWidth(this.width);
28446         }
28447         if (this.height) {
28448             this.viewEl.setHeight(this.height);
28449         }
28450         //if(this.inputValue !== undefined){
28451         //this.setValue(this.value);
28452         
28453         
28454         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28455         
28456         
28457         this.grid.render();
28458         this.grid.getDataSource().on('remove', this.refreshValue, this);
28459         this.grid.getDataSource().on('update', this.refreshValue, this);
28460         this.grid.on('afteredit', this.refreshValue, this);
28461  
28462     },
28463      
28464     
28465     /**
28466      * Sets the value of the item. 
28467      * @param {String} either an object  or a string..
28468      */
28469     setValue : function(v){
28470         //this.value = v;
28471         v = v || []; // empty set..
28472         // this does not seem smart - it really only affects memoryproxy grids..
28473         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28474             var ds = this.grid.getDataSource();
28475             // assumes a json reader..
28476             var data = {}
28477             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28478             ds.loadData( data);
28479         }
28480         // clear selection so it does not get stale.
28481         if (this.grid.sm) { 
28482             this.grid.sm.clearSelections();
28483         }
28484         
28485         Roo.form.GridField.superclass.setValue.call(this, v);
28486         this.refreshValue();
28487         // should load data in the grid really....
28488     },
28489     
28490     // private
28491     refreshValue: function() {
28492          var val = [];
28493         this.grid.getDataSource().each(function(r) {
28494             val.push(r.data);
28495         });
28496         this.el.dom.value = Roo.encode(val);
28497     }
28498     
28499      
28500     
28501     
28502 });/*
28503  * Based on:
28504  * Ext JS Library 1.1.1
28505  * Copyright(c) 2006-2007, Ext JS, LLC.
28506  *
28507  * Originally Released Under LGPL - original licence link has changed is not relivant.
28508  *
28509  * Fork - LGPL
28510  * <script type="text/javascript">
28511  */
28512 /**
28513  * @class Roo.form.DisplayField
28514  * @extends Roo.form.Field
28515  * A generic Field to display non-editable data.
28516  * @cfg {Boolean} closable (true|false) default false
28517  * @constructor
28518  * Creates a new Display Field item.
28519  * @param {Object} config Configuration options
28520  */
28521 Roo.form.DisplayField = function(config){
28522     Roo.form.DisplayField.superclass.constructor.call(this, config);
28523     
28524     this.addEvents({
28525         /**
28526          * @event close
28527          * Fires after the click the close btn
28528              * @param {Roo.form.DisplayField} this
28529              */
28530         close : true
28531     });
28532 };
28533
28534 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28535     inputType:      'hidden',
28536     allowBlank:     true,
28537     readOnly:         true,
28538     
28539  
28540     /**
28541      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28542      */
28543     focusClass : undefined,
28544     /**
28545      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28546      */
28547     fieldClass: 'x-form-field',
28548     
28549      /**
28550      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28551      */
28552     valueRenderer: undefined,
28553     
28554     width: 100,
28555     /**
28556      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28557      * {tag: "input", type: "checkbox", autocomplete: "off"})
28558      */
28559      
28560  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28561  
28562     closable : false,
28563     
28564     onResize : function(){
28565         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28566         
28567     },
28568
28569     initEvents : function(){
28570         // Roo.form.Checkbox.superclass.initEvents.call(this);
28571         // has no events...
28572         
28573         if(this.closable){
28574             this.closeEl.on('click', this.onClose, this);
28575         }
28576        
28577     },
28578
28579
28580     getResizeEl : function(){
28581         return this.wrap;
28582     },
28583
28584     getPositionEl : function(){
28585         return this.wrap;
28586     },
28587
28588     // private
28589     onRender : function(ct, position){
28590         
28591         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28592         //if(this.inputValue !== undefined){
28593         this.wrap = this.el.wrap();
28594         
28595         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
28596         
28597         if(this.closable){
28598             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
28599         }
28600         
28601         if (this.bodyStyle) {
28602             this.viewEl.applyStyles(this.bodyStyle);
28603         }
28604         //this.viewEl.setStyle('padding', '2px');
28605         
28606         this.setValue(this.value);
28607         
28608     },
28609 /*
28610     // private
28611     initValue : Roo.emptyFn,
28612
28613   */
28614
28615         // private
28616     onClick : function(){
28617         
28618     },
28619
28620     /**
28621      * Sets the checked state of the checkbox.
28622      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28623      */
28624     setValue : function(v){
28625         this.value = v;
28626         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28627         // this might be called before we have a dom element..
28628         if (!this.viewEl) {
28629             return;
28630         }
28631         this.viewEl.dom.innerHTML = html;
28632         Roo.form.DisplayField.superclass.setValue.call(this, v);
28633
28634     },
28635     
28636     onClose : function(e)
28637     {
28638         e.preventDefault();
28639         
28640         this.fireEvent('close', this);
28641     }
28642 });/*
28643  * 
28644  * Licence- LGPL
28645  * 
28646  */
28647
28648 /**
28649  * @class Roo.form.DayPicker
28650  * @extends Roo.form.Field
28651  * A Day picker show [M] [T] [W] ....
28652  * @constructor
28653  * Creates a new Day Picker
28654  * @param {Object} config Configuration options
28655  */
28656 Roo.form.DayPicker= function(config){
28657     Roo.form.DayPicker.superclass.constructor.call(this, config);
28658      
28659 };
28660
28661 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
28662     /**
28663      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28664      */
28665     focusClass : undefined,
28666     /**
28667      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28668      */
28669     fieldClass: "x-form-field",
28670    
28671     /**
28672      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28673      * {tag: "input", type: "checkbox", autocomplete: "off"})
28674      */
28675     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
28676     
28677    
28678     actionMode : 'viewEl', 
28679     //
28680     // private
28681  
28682     inputType : 'hidden',
28683     
28684      
28685     inputElement: false, // real input element?
28686     basedOn: false, // ????
28687     
28688     isFormField: true, // not sure where this is needed!!!!
28689
28690     onResize : function(){
28691         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
28692         if(!this.boxLabel){
28693             this.el.alignTo(this.wrap, 'c-c');
28694         }
28695     },
28696
28697     initEvents : function(){
28698         Roo.form.Checkbox.superclass.initEvents.call(this);
28699         this.el.on("click", this.onClick,  this);
28700         this.el.on("change", this.onClick,  this);
28701     },
28702
28703
28704     getResizeEl : function(){
28705         return this.wrap;
28706     },
28707
28708     getPositionEl : function(){
28709         return this.wrap;
28710     },
28711
28712     
28713     // private
28714     onRender : function(ct, position){
28715         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
28716        
28717         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
28718         
28719         var r1 = '<table><tr>';
28720         var r2 = '<tr class="x-form-daypick-icons">';
28721         for (var i=0; i < 7; i++) {
28722             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
28723             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
28724         }
28725         
28726         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
28727         viewEl.select('img').on('click', this.onClick, this);
28728         this.viewEl = viewEl;   
28729         
28730         
28731         // this will not work on Chrome!!!
28732         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
28733         this.el.on('propertychange', this.setFromHidden,  this);  //ie
28734         
28735         
28736           
28737
28738     },
28739
28740     // private
28741     initValue : Roo.emptyFn,
28742
28743     /**
28744      * Returns the checked state of the checkbox.
28745      * @return {Boolean} True if checked, else false
28746      */
28747     getValue : function(){
28748         return this.el.dom.value;
28749         
28750     },
28751
28752         // private
28753     onClick : function(e){ 
28754         //this.setChecked(!this.checked);
28755         Roo.get(e.target).toggleClass('x-menu-item-checked');
28756         this.refreshValue();
28757         //if(this.el.dom.checked != this.checked){
28758         //    this.setValue(this.el.dom.checked);
28759        // }
28760     },
28761     
28762     // private
28763     refreshValue : function()
28764     {
28765         var val = '';
28766         this.viewEl.select('img',true).each(function(e,i,n)  {
28767             val += e.is(".x-menu-item-checked") ? String(n) : '';
28768         });
28769         this.setValue(val, true);
28770     },
28771
28772     /**
28773      * Sets the checked state of the checkbox.
28774      * On is always based on a string comparison between inputValue and the param.
28775      * @param {Boolean/String} value - the value to set 
28776      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
28777      */
28778     setValue : function(v,suppressEvent){
28779         if (!this.el.dom) {
28780             return;
28781         }
28782         var old = this.el.dom.value ;
28783         this.el.dom.value = v;
28784         if (suppressEvent) {
28785             return ;
28786         }
28787          
28788         // update display..
28789         this.viewEl.select('img',true).each(function(e,i,n)  {
28790             
28791             var on = e.is(".x-menu-item-checked");
28792             var newv = v.indexOf(String(n)) > -1;
28793             if (on != newv) {
28794                 e.toggleClass('x-menu-item-checked');
28795             }
28796             
28797         });
28798         
28799         
28800         this.fireEvent('change', this, v, old);
28801         
28802         
28803     },
28804    
28805     // handle setting of hidden value by some other method!!?!?
28806     setFromHidden: function()
28807     {
28808         if(!this.el){
28809             return;
28810         }
28811         //console.log("SET FROM HIDDEN");
28812         //alert('setFrom hidden');
28813         this.setValue(this.el.dom.value);
28814     },
28815     
28816     onDestroy : function()
28817     {
28818         if(this.viewEl){
28819             Roo.get(this.viewEl).remove();
28820         }
28821          
28822         Roo.form.DayPicker.superclass.onDestroy.call(this);
28823     }
28824
28825 });/*
28826  * RooJS Library 1.1.1
28827  * Copyright(c) 2008-2011  Alan Knowles
28828  *
28829  * License - LGPL
28830  */
28831  
28832
28833 /**
28834  * @class Roo.form.ComboCheck
28835  * @extends Roo.form.ComboBox
28836  * A combobox for multiple select items.
28837  *
28838  * FIXME - could do with a reset button..
28839  * 
28840  * @constructor
28841  * Create a new ComboCheck
28842  * @param {Object} config Configuration options
28843  */
28844 Roo.form.ComboCheck = function(config){
28845     Roo.form.ComboCheck.superclass.constructor.call(this, config);
28846     // should verify some data...
28847     // like
28848     // hiddenName = required..
28849     // displayField = required
28850     // valudField == required
28851     var req= [ 'hiddenName', 'displayField', 'valueField' ];
28852     var _t = this;
28853     Roo.each(req, function(e) {
28854         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
28855             throw "Roo.form.ComboCheck : missing value for: " + e;
28856         }
28857     });
28858     
28859     
28860 };
28861
28862 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
28863      
28864      
28865     editable : false,
28866      
28867     selectedClass: 'x-menu-item-checked', 
28868     
28869     // private
28870     onRender : function(ct, position){
28871         var _t = this;
28872         
28873         
28874         
28875         if(!this.tpl){
28876             var cls = 'x-combo-list';
28877
28878             
28879             this.tpl =  new Roo.Template({
28880                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
28881                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
28882                    '<span>{' + this.displayField + '}</span>' +
28883                     '</div>' 
28884                 
28885             });
28886         }
28887  
28888         
28889         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
28890         this.view.singleSelect = false;
28891         this.view.multiSelect = true;
28892         this.view.toggleSelect = true;
28893         this.pageTb.add(new Roo.Toolbar.Fill(), {
28894             
28895             text: 'Done',
28896             handler: function()
28897             {
28898                 _t.collapse();
28899             }
28900         });
28901     },
28902     
28903     onViewOver : function(e, t){
28904         // do nothing...
28905         return;
28906         
28907     },
28908     
28909     onViewClick : function(doFocus,index){
28910         return;
28911         
28912     },
28913     select: function () {
28914         //Roo.log("SELECT CALLED");
28915     },
28916      
28917     selectByValue : function(xv, scrollIntoView){
28918         var ar = this.getValueArray();
28919         var sels = [];
28920         
28921         Roo.each(ar, function(v) {
28922             if(v === undefined || v === null){
28923                 return;
28924             }
28925             var r = this.findRecord(this.valueField, v);
28926             if(r){
28927                 sels.push(this.store.indexOf(r))
28928                 
28929             }
28930         },this);
28931         this.view.select(sels);
28932         return false;
28933     },
28934     
28935     
28936     
28937     onSelect : function(record, index){
28938        // Roo.log("onselect Called");
28939        // this is only called by the clear button now..
28940         this.view.clearSelections();
28941         this.setValue('[]');
28942         if (this.value != this.valueBefore) {
28943             this.fireEvent('change', this, this.value, this.valueBefore);
28944             this.valueBefore = this.value;
28945         }
28946     },
28947     getValueArray : function()
28948     {
28949         var ar = [] ;
28950         
28951         try {
28952             //Roo.log(this.value);
28953             if (typeof(this.value) == 'undefined') {
28954                 return [];
28955             }
28956             var ar = Roo.decode(this.value);
28957             return  ar instanceof Array ? ar : []; //?? valid?
28958             
28959         } catch(e) {
28960             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
28961             return [];
28962         }
28963          
28964     },
28965     expand : function ()
28966     {
28967         
28968         Roo.form.ComboCheck.superclass.expand.call(this);
28969         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
28970         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
28971         
28972
28973     },
28974     
28975     collapse : function(){
28976         Roo.form.ComboCheck.superclass.collapse.call(this);
28977         var sl = this.view.getSelectedIndexes();
28978         var st = this.store;
28979         var nv = [];
28980         var tv = [];
28981         var r;
28982         Roo.each(sl, function(i) {
28983             r = st.getAt(i);
28984             nv.push(r.get(this.valueField));
28985         },this);
28986         this.setValue(Roo.encode(nv));
28987         if (this.value != this.valueBefore) {
28988
28989             this.fireEvent('change', this, this.value, this.valueBefore);
28990             this.valueBefore = this.value;
28991         }
28992         
28993     },
28994     
28995     setValue : function(v){
28996         // Roo.log(v);
28997         this.value = v;
28998         
28999         var vals = this.getValueArray();
29000         var tv = [];
29001         Roo.each(vals, function(k) {
29002             var r = this.findRecord(this.valueField, k);
29003             if(r){
29004                 tv.push(r.data[this.displayField]);
29005             }else if(this.valueNotFoundText !== undefined){
29006                 tv.push( this.valueNotFoundText );
29007             }
29008         },this);
29009        // Roo.log(tv);
29010         
29011         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
29012         this.hiddenField.value = v;
29013         this.value = v;
29014     }
29015     
29016 });/*
29017  * Based on:
29018  * Ext JS Library 1.1.1
29019  * Copyright(c) 2006-2007, Ext JS, LLC.
29020  *
29021  * Originally Released Under LGPL - original licence link has changed is not relivant.
29022  *
29023  * Fork - LGPL
29024  * <script type="text/javascript">
29025  */
29026  
29027 /**
29028  * @class Roo.form.Signature
29029  * @extends Roo.form.Field
29030  * Signature field.  
29031  * @constructor
29032  * 
29033  * @param {Object} config Configuration options
29034  */
29035
29036 Roo.form.Signature = function(config){
29037     Roo.form.Signature.superclass.constructor.call(this, config);
29038     
29039     this.addEvents({// not in used??
29040          /**
29041          * @event confirm
29042          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
29043              * @param {Roo.form.Signature} combo This combo box
29044              */
29045         'confirm' : true,
29046         /**
29047          * @event reset
29048          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
29049              * @param {Roo.form.ComboBox} combo This combo box
29050              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
29051              */
29052         'reset' : true
29053     });
29054 };
29055
29056 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
29057     /**
29058      * @cfg {Object} labels Label to use when rendering a form.
29059      * defaults to 
29060      * labels : { 
29061      *      clear : "Clear",
29062      *      confirm : "Confirm"
29063      *  }
29064      */
29065     labels : { 
29066         clear : "Clear",
29067         confirm : "Confirm"
29068     },
29069     /**
29070      * @cfg {Number} width The signature panel width (defaults to 300)
29071      */
29072     width: 300,
29073     /**
29074      * @cfg {Number} height The signature panel height (defaults to 100)
29075      */
29076     height : 100,
29077     /**
29078      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
29079      */
29080     allowBlank : false,
29081     
29082     //private
29083     // {Object} signPanel The signature SVG panel element (defaults to {})
29084     signPanel : {},
29085     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
29086     isMouseDown : false,
29087     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
29088     isConfirmed : false,
29089     // {String} signatureTmp SVG mapping string (defaults to empty string)
29090     signatureTmp : '',
29091     
29092     
29093     defaultAutoCreate : { // modified by initCompnoent..
29094         tag: "input",
29095         type:"hidden"
29096     },
29097
29098     // private
29099     onRender : function(ct, position){
29100         
29101         Roo.form.Signature.superclass.onRender.call(this, ct, position);
29102         
29103         this.wrap = this.el.wrap({
29104             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
29105         });
29106         
29107         this.createToolbar(this);
29108         this.signPanel = this.wrap.createChild({
29109                 tag: 'div',
29110                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
29111             }, this.el
29112         );
29113             
29114         this.svgID = Roo.id();
29115         this.svgEl = this.signPanel.createChild({
29116               xmlns : 'http://www.w3.org/2000/svg',
29117               tag : 'svg',
29118               id : this.svgID + "-svg",
29119               width: this.width,
29120               height: this.height,
29121               viewBox: '0 0 '+this.width+' '+this.height,
29122               cn : [
29123                 {
29124                     tag: "rect",
29125                     id: this.svgID + "-svg-r",
29126                     width: this.width,
29127                     height: this.height,
29128                     fill: "#ffa"
29129                 },
29130                 {
29131                     tag: "line",
29132                     id: this.svgID + "-svg-l",
29133                     x1: "0", // start
29134                     y1: (this.height*0.8), // start set the line in 80% of height
29135                     x2: this.width, // end
29136                     y2: (this.height*0.8), // end set the line in 80% of height
29137                     'stroke': "#666",
29138                     'stroke-width': "1",
29139                     'stroke-dasharray': "3",
29140                     'shape-rendering': "crispEdges",
29141                     'pointer-events': "none"
29142                 },
29143                 {
29144                     tag: "path",
29145                     id: this.svgID + "-svg-p",
29146                     'stroke': "navy",
29147                     'stroke-width': "3",
29148                     'fill': "none",
29149                     'pointer-events': 'none'
29150                 }
29151               ]
29152         });
29153         this.createSVG();
29154         this.svgBox = this.svgEl.dom.getScreenCTM();
29155     },
29156     createSVG : function(){ 
29157         var svg = this.signPanel;
29158         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
29159         var t = this;
29160
29161         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
29162         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
29163         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
29164         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
29165         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
29166         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
29167         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
29168         
29169     },
29170     isTouchEvent : function(e){
29171         return e.type.match(/^touch/);
29172     },
29173     getCoords : function (e) {
29174         var pt    = this.svgEl.dom.createSVGPoint();
29175         pt.x = e.clientX; 
29176         pt.y = e.clientY;
29177         if (this.isTouchEvent(e)) {
29178             pt.x =  e.targetTouches[0].clientX;
29179             pt.y = e.targetTouches[0].clientY;
29180         }
29181         var a = this.svgEl.dom.getScreenCTM();
29182         var b = a.inverse();
29183         var mx = pt.matrixTransform(b);
29184         return mx.x + ',' + mx.y;
29185     },
29186     //mouse event headler 
29187     down : function (e) {
29188         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
29189         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
29190         
29191         this.isMouseDown = true;
29192         
29193         e.preventDefault();
29194     },
29195     move : function (e) {
29196         if (this.isMouseDown) {
29197             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
29198             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
29199         }
29200         
29201         e.preventDefault();
29202     },
29203     up : function (e) {
29204         this.isMouseDown = false;
29205         var sp = this.signatureTmp.split(' ');
29206         
29207         if(sp.length > 1){
29208             if(!sp[sp.length-2].match(/^L/)){
29209                 sp.pop();
29210                 sp.pop();
29211                 sp.push("");
29212                 this.signatureTmp = sp.join(" ");
29213             }
29214         }
29215         if(this.getValue() != this.signatureTmp){
29216             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
29217             this.isConfirmed = false;
29218         }
29219         e.preventDefault();
29220     },
29221     
29222     /**
29223      * Protected method that will not generally be called directly. It
29224      * is called when the editor creates its toolbar. Override this method if you need to
29225      * add custom toolbar buttons.
29226      * @param {HtmlEditor} editor
29227      */
29228     createToolbar : function(editor){
29229          function btn(id, toggle, handler){
29230             var xid = fid + '-'+ id ;
29231             return {
29232                 id : xid,
29233                 cmd : id,
29234                 cls : 'x-btn-icon x-edit-'+id,
29235                 enableToggle:toggle !== false,
29236                 scope: editor, // was editor...
29237                 handler:handler||editor.relayBtnCmd,
29238                 clickEvent:'mousedown',
29239                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
29240                 tabIndex:-1
29241             };
29242         }
29243         
29244         
29245         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
29246         this.tb = tb;
29247         this.tb.add(
29248            {
29249                 cls : ' x-signature-btn x-signature-'+id,
29250                 scope: editor, // was editor...
29251                 handler: this.reset,
29252                 clickEvent:'mousedown',
29253                 text: this.labels.clear
29254             },
29255             {
29256                  xtype : 'Fill',
29257                  xns: Roo.Toolbar
29258             }, 
29259             {
29260                 cls : '  x-signature-btn x-signature-'+id,
29261                 scope: editor, // was editor...
29262                 handler: this.confirmHandler,
29263                 clickEvent:'mousedown',
29264                 text: this.labels.confirm
29265             }
29266         );
29267     
29268     },
29269     //public
29270     /**
29271      * when user is clicked confirm then show this image.....
29272      * 
29273      * @return {String} Image Data URI
29274      */
29275     getImageDataURI : function(){
29276         var svg = this.svgEl.dom.parentNode.innerHTML;
29277         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
29278         return src; 
29279     },
29280     /**
29281      * 
29282      * @return {Boolean} this.isConfirmed
29283      */
29284     getConfirmed : function(){
29285         return this.isConfirmed;
29286     },
29287     /**
29288      * 
29289      * @return {Number} this.width
29290      */
29291     getWidth : function(){
29292         return this.width;
29293     },
29294     /**
29295      * 
29296      * @return {Number} this.height
29297      */
29298     getHeight : function(){
29299         return this.height;
29300     },
29301     // private
29302     getSignature : function(){
29303         return this.signatureTmp;
29304     },
29305     // private
29306     reset : function(){
29307         this.signatureTmp = '';
29308         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
29309         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
29310         this.isConfirmed = false;
29311         Roo.form.Signature.superclass.reset.call(this);
29312     },
29313     setSignature : function(s){
29314         this.signatureTmp = s;
29315         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
29316         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
29317         this.setValue(s);
29318         this.isConfirmed = false;
29319         Roo.form.Signature.superclass.reset.call(this);
29320     }, 
29321     test : function(){
29322 //        Roo.log(this.signPanel.dom.contentWindow.up())
29323     },
29324     //private
29325     setConfirmed : function(){
29326         
29327         
29328         
29329 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
29330     },
29331     // private
29332     confirmHandler : function(){
29333         if(!this.getSignature()){
29334             return;
29335         }
29336         
29337         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
29338         this.setValue(this.getSignature());
29339         this.isConfirmed = true;
29340         
29341         this.fireEvent('confirm', this);
29342     },
29343     // private
29344     // Subclasses should provide the validation implementation by overriding this
29345     validateValue : function(value){
29346         if(this.allowBlank){
29347             return true;
29348         }
29349         
29350         if(this.isConfirmed){
29351             return true;
29352         }
29353         return false;
29354     }
29355 });/*
29356  * Based on:
29357  * Ext JS Library 1.1.1
29358  * Copyright(c) 2006-2007, Ext JS, LLC.
29359  *
29360  * Originally Released Under LGPL - original licence link has changed is not relivant.
29361  *
29362  * Fork - LGPL
29363  * <script type="text/javascript">
29364  */
29365  
29366
29367 /**
29368  * @class Roo.form.ComboBox
29369  * @extends Roo.form.TriggerField
29370  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
29371  * @constructor
29372  * Create a new ComboBox.
29373  * @param {Object} config Configuration options
29374  */
29375 Roo.form.Select = function(config){
29376     Roo.form.Select.superclass.constructor.call(this, config);
29377      
29378 };
29379
29380 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
29381     /**
29382      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
29383      */
29384     /**
29385      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
29386      * rendering into an Roo.Editor, defaults to false)
29387      */
29388     /**
29389      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
29390      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
29391      */
29392     /**
29393      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
29394      */
29395     /**
29396      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
29397      * the dropdown list (defaults to undefined, with no header element)
29398      */
29399
29400      /**
29401      * @cfg {String/Roo.Template} tpl The template to use to render the output
29402      */
29403      
29404     // private
29405     defaultAutoCreate : {tag: "select"  },
29406     /**
29407      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
29408      */
29409     listWidth: undefined,
29410     /**
29411      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
29412      * mode = 'remote' or 'text' if mode = 'local')
29413      */
29414     displayField: undefined,
29415     /**
29416      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
29417      * mode = 'remote' or 'value' if mode = 'local'). 
29418      * Note: use of a valueField requires the user make a selection
29419      * in order for a value to be mapped.
29420      */
29421     valueField: undefined,
29422     
29423     
29424     /**
29425      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
29426      * field's data value (defaults to the underlying DOM element's name)
29427      */
29428     hiddenName: undefined,
29429     /**
29430      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
29431      */
29432     listClass: '',
29433     /**
29434      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
29435      */
29436     selectedClass: 'x-combo-selected',
29437     /**
29438      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
29439      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
29440      * which displays a downward arrow icon).
29441      */
29442     triggerClass : 'x-form-arrow-trigger',
29443     /**
29444      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
29445      */
29446     shadow:'sides',
29447     /**
29448      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
29449      * anchor positions (defaults to 'tl-bl')
29450      */
29451     listAlign: 'tl-bl?',
29452     /**
29453      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
29454      */
29455     maxHeight: 300,
29456     /**
29457      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
29458      * query specified by the allQuery config option (defaults to 'query')
29459      */
29460     triggerAction: 'query',
29461     /**
29462      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
29463      * (defaults to 4, does not apply if editable = false)
29464      */
29465     minChars : 4,
29466     /**
29467      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
29468      * delay (typeAheadDelay) if it matches a known value (defaults to false)
29469      */
29470     typeAhead: false,
29471     /**
29472      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
29473      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
29474      */
29475     queryDelay: 500,
29476     /**
29477      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
29478      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
29479      */
29480     pageSize: 0,
29481     /**
29482      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
29483      * when editable = true (defaults to false)
29484      */
29485     selectOnFocus:false,
29486     /**
29487      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
29488      */
29489     queryParam: 'query',
29490     /**
29491      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
29492      * when mode = 'remote' (defaults to 'Loading...')
29493      */
29494     loadingText: 'Loading...',
29495     /**
29496      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
29497      */
29498     resizable: false,
29499     /**
29500      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
29501      */
29502     handleHeight : 8,
29503     /**
29504      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
29505      * traditional select (defaults to true)
29506      */
29507     editable: true,
29508     /**
29509      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
29510      */
29511     allQuery: '',
29512     /**
29513      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
29514      */
29515     mode: 'remote',
29516     /**
29517      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
29518      * listWidth has a higher value)
29519      */
29520     minListWidth : 70,
29521     /**
29522      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
29523      * allow the user to set arbitrary text into the field (defaults to false)
29524      */
29525     forceSelection:false,
29526     /**
29527      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
29528      * if typeAhead = true (defaults to 250)
29529      */
29530     typeAheadDelay : 250,
29531     /**
29532      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
29533      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
29534      */
29535     valueNotFoundText : undefined,
29536     
29537     /**
29538      * @cfg {String} defaultValue The value displayed after loading the store.
29539      */
29540     defaultValue: '',
29541     
29542     /**
29543      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
29544      */
29545     blockFocus : false,
29546     
29547     /**
29548      * @cfg {Boolean} disableClear Disable showing of clear button.
29549      */
29550     disableClear : false,
29551     /**
29552      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
29553      */
29554     alwaysQuery : false,
29555     
29556     //private
29557     addicon : false,
29558     editicon: false,
29559     
29560     // element that contains real text value.. (when hidden is used..)
29561      
29562     // private
29563     onRender : function(ct, position){
29564         Roo.form.Field.prototype.onRender.call(this, ct, position);
29565         
29566         if(this.store){
29567             this.store.on('beforeload', this.onBeforeLoad, this);
29568             this.store.on('load', this.onLoad, this);
29569             this.store.on('loadexception', this.onLoadException, this);
29570             this.store.load({});
29571         }
29572         
29573         
29574         
29575     },
29576
29577     // private
29578     initEvents : function(){
29579         //Roo.form.ComboBox.superclass.initEvents.call(this);
29580  
29581     },
29582
29583     onDestroy : function(){
29584        
29585         if(this.store){
29586             this.store.un('beforeload', this.onBeforeLoad, this);
29587             this.store.un('load', this.onLoad, this);
29588             this.store.un('loadexception', this.onLoadException, this);
29589         }
29590         //Roo.form.ComboBox.superclass.onDestroy.call(this);
29591     },
29592
29593     // private
29594     fireKey : function(e){
29595         if(e.isNavKeyPress() && !this.list.isVisible()){
29596             this.fireEvent("specialkey", this, e);
29597         }
29598     },
29599
29600     // private
29601     onResize: function(w, h){
29602         
29603         return; 
29604     
29605         
29606     },
29607
29608     /**
29609      * Allow or prevent the user from directly editing the field text.  If false is passed,
29610      * the user will only be able to select from the items defined in the dropdown list.  This method
29611      * is the runtime equivalent of setting the 'editable' config option at config time.
29612      * @param {Boolean} value True to allow the user to directly edit the field text
29613      */
29614     setEditable : function(value){
29615          
29616     },
29617
29618     // private
29619     onBeforeLoad : function(){
29620         
29621         Roo.log("Select before load");
29622         return;
29623     
29624         this.innerList.update(this.loadingText ?
29625                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
29626         //this.restrictHeight();
29627         this.selectedIndex = -1;
29628     },
29629
29630     // private
29631     onLoad : function(){
29632
29633     
29634         var dom = this.el.dom;
29635         dom.innerHTML = '';
29636          var od = dom.ownerDocument;
29637          
29638         if (this.emptyText) {
29639             var op = od.createElement('option');
29640             op.setAttribute('value', '');
29641             op.innerHTML = String.format('{0}', this.emptyText);
29642             dom.appendChild(op);
29643         }
29644         if(this.store.getCount() > 0){
29645            
29646             var vf = this.valueField;
29647             var df = this.displayField;
29648             this.store.data.each(function(r) {
29649                 // which colmsn to use... testing - cdoe / title..
29650                 var op = od.createElement('option');
29651                 op.setAttribute('value', r.data[vf]);
29652                 op.innerHTML = String.format('{0}', r.data[df]);
29653                 dom.appendChild(op);
29654             });
29655             if (typeof(this.defaultValue != 'undefined')) {
29656                 this.setValue(this.defaultValue);
29657             }
29658             
29659              
29660         }else{
29661             //this.onEmptyResults();
29662         }
29663         //this.el.focus();
29664     },
29665     // private
29666     onLoadException : function()
29667     {
29668         dom.innerHTML = '';
29669             
29670         Roo.log("Select on load exception");
29671         return;
29672     
29673         this.collapse();
29674         Roo.log(this.store.reader.jsonData);
29675         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
29676             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
29677         }
29678         
29679         
29680     },
29681     // private
29682     onTypeAhead : function(){
29683          
29684     },
29685
29686     // private
29687     onSelect : function(record, index){
29688         Roo.log('on select?');
29689         return;
29690         if(this.fireEvent('beforeselect', this, record, index) !== false){
29691             this.setFromData(index > -1 ? record.data : false);
29692             this.collapse();
29693             this.fireEvent('select', this, record, index);
29694         }
29695     },
29696
29697     /**
29698      * Returns the currently selected field value or empty string if no value is set.
29699      * @return {String} value The selected value
29700      */
29701     getValue : function(){
29702         var dom = this.el.dom;
29703         this.value = dom.options[dom.selectedIndex].value;
29704         return this.value;
29705         
29706     },
29707
29708     /**
29709      * Clears any text/value currently set in the field
29710      */
29711     clearValue : function(){
29712         this.value = '';
29713         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
29714         
29715     },
29716
29717     /**
29718      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
29719      * will be displayed in the field.  If the value does not match the data value of an existing item,
29720      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
29721      * Otherwise the field will be blank (although the value will still be set).
29722      * @param {String} value The value to match
29723      */
29724     setValue : function(v){
29725         var d = this.el.dom;
29726         for (var i =0; i < d.options.length;i++) {
29727             if (v == d.options[i].value) {
29728                 d.selectedIndex = i;
29729                 this.value = v;
29730                 return;
29731             }
29732         }
29733         this.clearValue();
29734     },
29735     /**
29736      * @property {Object} the last set data for the element
29737      */
29738     
29739     lastData : false,
29740     /**
29741      * Sets the value of the field based on a object which is related to the record format for the store.
29742      * @param {Object} value the value to set as. or false on reset?
29743      */
29744     setFromData : function(o){
29745         Roo.log('setfrom data?');
29746          
29747         
29748         
29749     },
29750     // private
29751     reset : function(){
29752         this.clearValue();
29753     },
29754     // private
29755     findRecord : function(prop, value){
29756         
29757         return false;
29758     
29759         var record;
29760         if(this.store.getCount() > 0){
29761             this.store.each(function(r){
29762                 if(r.data[prop] == value){
29763                     record = r;
29764                     return false;
29765                 }
29766                 return true;
29767             });
29768         }
29769         return record;
29770     },
29771     
29772     getName: function()
29773     {
29774         // returns hidden if it's set..
29775         if (!this.rendered) {return ''};
29776         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
29777         
29778     },
29779      
29780
29781     
29782
29783     // private
29784     onEmptyResults : function(){
29785         Roo.log('empty results');
29786         //this.collapse();
29787     },
29788
29789     /**
29790      * Returns true if the dropdown list is expanded, else false.
29791      */
29792     isExpanded : function(){
29793         return false;
29794     },
29795
29796     /**
29797      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
29798      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
29799      * @param {String} value The data value of the item to select
29800      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
29801      * selected item if it is not currently in view (defaults to true)
29802      * @return {Boolean} True if the value matched an item in the list, else false
29803      */
29804     selectByValue : function(v, scrollIntoView){
29805         Roo.log('select By Value');
29806         return false;
29807     
29808         if(v !== undefined && v !== null){
29809             var r = this.findRecord(this.valueField || this.displayField, v);
29810             if(r){
29811                 this.select(this.store.indexOf(r), scrollIntoView);
29812                 return true;
29813             }
29814         }
29815         return false;
29816     },
29817
29818     /**
29819      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
29820      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
29821      * @param {Number} index The zero-based index of the list item to select
29822      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
29823      * selected item if it is not currently in view (defaults to true)
29824      */
29825     select : function(index, scrollIntoView){
29826         Roo.log('select ');
29827         return  ;
29828         
29829         this.selectedIndex = index;
29830         this.view.select(index);
29831         if(scrollIntoView !== false){
29832             var el = this.view.getNode(index);
29833             if(el){
29834                 this.innerList.scrollChildIntoView(el, false);
29835             }
29836         }
29837     },
29838
29839       
29840
29841     // private
29842     validateBlur : function(){
29843         
29844         return;
29845         
29846     },
29847
29848     // private
29849     initQuery : function(){
29850         this.doQuery(this.getRawValue());
29851     },
29852
29853     // private
29854     doForce : function(){
29855         if(this.el.dom.value.length > 0){
29856             this.el.dom.value =
29857                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
29858              
29859         }
29860     },
29861
29862     /**
29863      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
29864      * query allowing the query action to be canceled if needed.
29865      * @param {String} query The SQL query to execute
29866      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
29867      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
29868      * saved in the current store (defaults to false)
29869      */
29870     doQuery : function(q, forceAll){
29871         
29872         Roo.log('doQuery?');
29873         if(q === undefined || q === null){
29874             q = '';
29875         }
29876         var qe = {
29877             query: q,
29878             forceAll: forceAll,
29879             combo: this,
29880             cancel:false
29881         };
29882         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
29883             return false;
29884         }
29885         q = qe.query;
29886         forceAll = qe.forceAll;
29887         if(forceAll === true || (q.length >= this.minChars)){
29888             if(this.lastQuery != q || this.alwaysQuery){
29889                 this.lastQuery = q;
29890                 if(this.mode == 'local'){
29891                     this.selectedIndex = -1;
29892                     if(forceAll){
29893                         this.store.clearFilter();
29894                     }else{
29895                         this.store.filter(this.displayField, q);
29896                     }
29897                     this.onLoad();
29898                 }else{
29899                     this.store.baseParams[this.queryParam] = q;
29900                     this.store.load({
29901                         params: this.getParams(q)
29902                     });
29903                     this.expand();
29904                 }
29905             }else{
29906                 this.selectedIndex = -1;
29907                 this.onLoad();   
29908             }
29909         }
29910     },
29911
29912     // private
29913     getParams : function(q){
29914         var p = {};
29915         //p[this.queryParam] = q;
29916         if(this.pageSize){
29917             p.start = 0;
29918             p.limit = this.pageSize;
29919         }
29920         return p;
29921     },
29922
29923     /**
29924      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
29925      */
29926     collapse : function(){
29927         
29928     },
29929
29930     // private
29931     collapseIf : function(e){
29932         
29933     },
29934
29935     /**
29936      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
29937      */
29938     expand : function(){
29939         
29940     } ,
29941
29942     // private
29943      
29944
29945     /** 
29946     * @cfg {Boolean} grow 
29947     * @hide 
29948     */
29949     /** 
29950     * @cfg {Number} growMin 
29951     * @hide 
29952     */
29953     /** 
29954     * @cfg {Number} growMax 
29955     * @hide 
29956     */
29957     /**
29958      * @hide
29959      * @method autoSize
29960      */
29961     
29962     setWidth : function()
29963     {
29964         
29965     },
29966     getResizeEl : function(){
29967         return this.el;
29968     }
29969 });//<script type="text/javasscript">
29970  
29971
29972 /**
29973  * @class Roo.DDView
29974  * A DnD enabled version of Roo.View.
29975  * @param {Element/String} container The Element in which to create the View.
29976  * @param {String} tpl The template string used to create the markup for each element of the View
29977  * @param {Object} config The configuration properties. These include all the config options of
29978  * {@link Roo.View} plus some specific to this class.<br>
29979  * <p>
29980  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
29981  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
29982  * <p>
29983  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
29984 .x-view-drag-insert-above {
29985         border-top:1px dotted #3366cc;
29986 }
29987 .x-view-drag-insert-below {
29988         border-bottom:1px dotted #3366cc;
29989 }
29990 </code></pre>
29991  * 
29992  */
29993  
29994 Roo.DDView = function(container, tpl, config) {
29995     Roo.DDView.superclass.constructor.apply(this, arguments);
29996     this.getEl().setStyle("outline", "0px none");
29997     this.getEl().unselectable();
29998     if (this.dragGroup) {
29999         this.setDraggable(this.dragGroup.split(","));
30000     }
30001     if (this.dropGroup) {
30002         this.setDroppable(this.dropGroup.split(","));
30003     }
30004     if (this.deletable) {
30005         this.setDeletable();
30006     }
30007     this.isDirtyFlag = false;
30008         this.addEvents({
30009                 "drop" : true
30010         });
30011 };
30012
30013 Roo.extend(Roo.DDView, Roo.View, {
30014 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
30015 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
30016 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
30017 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
30018
30019         isFormField: true,
30020
30021         reset: Roo.emptyFn,
30022         
30023         clearInvalid: Roo.form.Field.prototype.clearInvalid,
30024
30025         validate: function() {
30026                 return true;
30027         },
30028         
30029         destroy: function() {
30030                 this.purgeListeners();
30031                 this.getEl.removeAllListeners();
30032                 this.getEl().remove();
30033                 if (this.dragZone) {
30034                         if (this.dragZone.destroy) {
30035                                 this.dragZone.destroy();
30036                         }
30037                 }
30038                 if (this.dropZone) {
30039                         if (this.dropZone.destroy) {
30040                                 this.dropZone.destroy();
30041                         }
30042                 }
30043         },
30044
30045 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
30046         getName: function() {
30047                 return this.name;
30048         },
30049
30050 /**     Loads the View from a JSON string representing the Records to put into the Store. */
30051         setValue: function(v) {
30052                 if (!this.store) {
30053                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
30054                 }
30055                 var data = {};
30056                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
30057                 this.store.proxy = new Roo.data.MemoryProxy(data);
30058                 this.store.load();
30059         },
30060
30061 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
30062         getValue: function() {
30063                 var result = '(';
30064                 this.store.each(function(rec) {
30065                         result += rec.id + ',';
30066                 });
30067                 return result.substr(0, result.length - 1) + ')';
30068         },
30069         
30070         getIds: function() {
30071                 var i = 0, result = new Array(this.store.getCount());
30072                 this.store.each(function(rec) {
30073                         result[i++] = rec.id;
30074                 });
30075                 return result;
30076         },
30077         
30078         isDirty: function() {
30079                 return this.isDirtyFlag;
30080         },
30081
30082 /**
30083  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
30084  *      whole Element becomes the target, and this causes the drop gesture to append.
30085  */
30086     getTargetFromEvent : function(e) {
30087                 var target = e.getTarget();
30088                 while ((target !== null) && (target.parentNode != this.el.dom)) {
30089                 target = target.parentNode;
30090                 }
30091                 if (!target) {
30092                         target = this.el.dom.lastChild || this.el.dom;
30093                 }
30094                 return target;
30095     },
30096
30097 /**
30098  *      Create the drag data which consists of an object which has the property "ddel" as
30099  *      the drag proxy element. 
30100  */
30101     getDragData : function(e) {
30102         var target = this.findItemFromChild(e.getTarget());
30103                 if(target) {
30104                         this.handleSelection(e);
30105                         var selNodes = this.getSelectedNodes();
30106             var dragData = {
30107                 source: this,
30108                 copy: this.copy || (this.allowCopy && e.ctrlKey),
30109                 nodes: selNodes,
30110                 records: []
30111                         };
30112                         var selectedIndices = this.getSelectedIndexes();
30113                         for (var i = 0; i < selectedIndices.length; i++) {
30114                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
30115                         }
30116                         if (selNodes.length == 1) {
30117                                 dragData.ddel = target.cloneNode(true); // the div element
30118                         } else {
30119                                 var div = document.createElement('div'); // create the multi element drag "ghost"
30120                                 div.className = 'multi-proxy';
30121                                 for (var i = 0, len = selNodes.length; i < len; i++) {
30122                                         div.appendChild(selNodes[i].cloneNode(true));
30123                                 }
30124                                 dragData.ddel = div;
30125                         }
30126             //console.log(dragData)
30127             //console.log(dragData.ddel.innerHTML)
30128                         return dragData;
30129                 }
30130         //console.log('nodragData')
30131                 return false;
30132     },
30133     
30134 /**     Specify to which ddGroup items in this DDView may be dragged. */
30135     setDraggable: function(ddGroup) {
30136         if (ddGroup instanceof Array) {
30137                 Roo.each(ddGroup, this.setDraggable, this);
30138                 return;
30139         }
30140         if (this.dragZone) {
30141                 this.dragZone.addToGroup(ddGroup);
30142         } else {
30143                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
30144                                 containerScroll: true,
30145                                 ddGroup: ddGroup 
30146
30147                         });
30148 //                      Draggability implies selection. DragZone's mousedown selects the element.
30149                         if (!this.multiSelect) { this.singleSelect = true; }
30150
30151 //                      Wire the DragZone's handlers up to methods in *this*
30152                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
30153                 }
30154     },
30155
30156 /**     Specify from which ddGroup this DDView accepts drops. */
30157     setDroppable: function(ddGroup) {
30158         if (ddGroup instanceof Array) {
30159                 Roo.each(ddGroup, this.setDroppable, this);
30160                 return;
30161         }
30162         if (this.dropZone) {
30163                 this.dropZone.addToGroup(ddGroup);
30164         } else {
30165                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
30166                                 containerScroll: true,
30167                                 ddGroup: ddGroup
30168                         });
30169
30170 //                      Wire the DropZone's handlers up to methods in *this*
30171                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
30172                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
30173                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
30174                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
30175                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
30176                 }
30177     },
30178
30179 /**     Decide whether to drop above or below a View node. */
30180     getDropPoint : function(e, n, dd){
30181         if (n == this.el.dom) { return "above"; }
30182                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
30183                 var c = t + (b - t) / 2;
30184                 var y = Roo.lib.Event.getPageY(e);
30185                 if(y <= c) {
30186                         return "above";
30187                 }else{
30188                         return "below";
30189                 }
30190     },
30191
30192     onNodeEnter : function(n, dd, e, data){
30193                 return false;
30194     },
30195     
30196     onNodeOver : function(n, dd, e, data){
30197                 var pt = this.getDropPoint(e, n, dd);
30198                 // set the insert point style on the target node
30199                 var dragElClass = this.dropNotAllowed;
30200                 if (pt) {
30201                         var targetElClass;
30202                         if (pt == "above"){
30203                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
30204                                 targetElClass = "x-view-drag-insert-above";
30205                         } else {
30206                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
30207                                 targetElClass = "x-view-drag-insert-below";
30208                         }
30209                         if (this.lastInsertClass != targetElClass){
30210                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
30211                                 this.lastInsertClass = targetElClass;
30212                         }
30213                 }
30214                 return dragElClass;
30215         },
30216
30217     onNodeOut : function(n, dd, e, data){
30218                 this.removeDropIndicators(n);
30219     },
30220
30221     onNodeDrop : function(n, dd, e, data){
30222         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
30223                 return false;
30224         }
30225         var pt = this.getDropPoint(e, n, dd);
30226                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
30227                 if (pt == "below") { insertAt++; }
30228                 for (var i = 0; i < data.records.length; i++) {
30229                         var r = data.records[i];
30230                         var dup = this.store.getById(r.id);
30231                         if (dup && (dd != this.dragZone)) {
30232                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
30233                         } else {
30234                                 if (data.copy) {
30235                                         this.store.insert(insertAt++, r.copy());
30236                                 } else {
30237                                         data.source.isDirtyFlag = true;
30238                                         r.store.remove(r);
30239                                         this.store.insert(insertAt++, r);
30240                                 }
30241                                 this.isDirtyFlag = true;
30242                         }
30243                 }
30244                 this.dragZone.cachedTarget = null;
30245                 return true;
30246     },
30247
30248     removeDropIndicators : function(n){
30249                 if(n){
30250                         Roo.fly(n).removeClass([
30251                                 "x-view-drag-insert-above",
30252                                 "x-view-drag-insert-below"]);
30253                         this.lastInsertClass = "_noclass";
30254                 }
30255     },
30256
30257 /**
30258  *      Utility method. Add a delete option to the DDView's context menu.
30259  *      @param {String} imageUrl The URL of the "delete" icon image.
30260  */
30261         setDeletable: function(imageUrl) {
30262                 if (!this.singleSelect && !this.multiSelect) {
30263                         this.singleSelect = true;
30264                 }
30265                 var c = this.getContextMenu();
30266                 this.contextMenu.on("itemclick", function(item) {
30267                         switch (item.id) {
30268                                 case "delete":
30269                                         this.remove(this.getSelectedIndexes());
30270                                         break;
30271                         }
30272                 }, this);
30273                 this.contextMenu.add({
30274                         icon: imageUrl,
30275                         id: "delete",
30276                         text: 'Delete'
30277                 });
30278         },
30279         
30280 /**     Return the context menu for this DDView. */
30281         getContextMenu: function() {
30282                 if (!this.contextMenu) {
30283 //                      Create the View's context menu
30284                         this.contextMenu = new Roo.menu.Menu({
30285                                 id: this.id + "-contextmenu"
30286                         });
30287                         this.el.on("contextmenu", this.showContextMenu, this);
30288                 }
30289                 return this.contextMenu;
30290         },
30291         
30292         disableContextMenu: function() {
30293                 if (this.contextMenu) {
30294                         this.el.un("contextmenu", this.showContextMenu, this);
30295                 }
30296         },
30297
30298         showContextMenu: function(e, item) {
30299         item = this.findItemFromChild(e.getTarget());
30300                 if (item) {
30301                         e.stopEvent();
30302                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
30303                         this.contextMenu.showAt(e.getXY());
30304             }
30305     },
30306
30307 /**
30308  *      Remove {@link Roo.data.Record}s at the specified indices.
30309  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
30310  */
30311     remove: function(selectedIndices) {
30312                 selectedIndices = [].concat(selectedIndices);
30313                 for (var i = 0; i < selectedIndices.length; i++) {
30314                         var rec = this.store.getAt(selectedIndices[i]);
30315                         this.store.remove(rec);
30316                 }
30317     },
30318
30319 /**
30320  *      Double click fires the event, but also, if this is draggable, and there is only one other
30321  *      related DropZone, it transfers the selected node.
30322  */
30323     onDblClick : function(e){
30324         var item = this.findItemFromChild(e.getTarget());
30325         if(item){
30326             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
30327                 return false;
30328             }
30329             if (this.dragGroup) {
30330                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
30331                     while (targets.indexOf(this.dropZone) > -1) {
30332                             targets.remove(this.dropZone);
30333                                 }
30334                     if (targets.length == 1) {
30335                                         this.dragZone.cachedTarget = null;
30336                         var el = Roo.get(targets[0].getEl());
30337                         var box = el.getBox(true);
30338                         targets[0].onNodeDrop(el.dom, {
30339                                 target: el.dom,
30340                                 xy: [box.x, box.y + box.height - 1]
30341                         }, null, this.getDragData(e));
30342                     }
30343                 }
30344         }
30345     },
30346     
30347     handleSelection: function(e) {
30348                 this.dragZone.cachedTarget = null;
30349         var item = this.findItemFromChild(e.getTarget());
30350         if (!item) {
30351                 this.clearSelections(true);
30352                 return;
30353         }
30354                 if (item && (this.multiSelect || this.singleSelect)){
30355                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
30356                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
30357                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
30358                                 this.unselect(item);
30359                         } else {
30360                                 this.select(item, this.multiSelect && e.ctrlKey);
30361                                 this.lastSelection = item;
30362                         }
30363                 }
30364     },
30365
30366     onItemClick : function(item, index, e){
30367                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
30368                         return false;
30369                 }
30370                 return true;
30371     },
30372
30373     unselect : function(nodeInfo, suppressEvent){
30374                 var node = this.getNode(nodeInfo);
30375                 if(node && this.isSelected(node)){
30376                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
30377                                 Roo.fly(node).removeClass(this.selectedClass);
30378                                 this.selections.remove(node);
30379                                 if(!suppressEvent){
30380                                         this.fireEvent("selectionchange", this, this.selections);
30381                                 }
30382                         }
30383                 }
30384     }
30385 });
30386 /*
30387  * Based on:
30388  * Ext JS Library 1.1.1
30389  * Copyright(c) 2006-2007, Ext JS, LLC.
30390  *
30391  * Originally Released Under LGPL - original licence link has changed is not relivant.
30392  *
30393  * Fork - LGPL
30394  * <script type="text/javascript">
30395  */
30396  
30397 /**
30398  * @class Roo.LayoutManager
30399  * @extends Roo.util.Observable
30400  * Base class for layout managers.
30401  */
30402 Roo.LayoutManager = function(container, config){
30403     Roo.LayoutManager.superclass.constructor.call(this);
30404     this.el = Roo.get(container);
30405     // ie scrollbar fix
30406     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
30407         document.body.scroll = "no";
30408     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
30409         this.el.position('relative');
30410     }
30411     this.id = this.el.id;
30412     this.el.addClass("x-layout-container");
30413     /** false to disable window resize monitoring @type Boolean */
30414     this.monitorWindowResize = true;
30415     this.regions = {};
30416     this.addEvents({
30417         /**
30418          * @event layout
30419          * Fires when a layout is performed. 
30420          * @param {Roo.LayoutManager} this
30421          */
30422         "layout" : true,
30423         /**
30424          * @event regionresized
30425          * Fires when the user resizes a region. 
30426          * @param {Roo.LayoutRegion} region The resized region
30427          * @param {Number} newSize The new size (width for east/west, height for north/south)
30428          */
30429         "regionresized" : true,
30430         /**
30431          * @event regioncollapsed
30432          * Fires when a region is collapsed. 
30433          * @param {Roo.LayoutRegion} region The collapsed region
30434          */
30435         "regioncollapsed" : true,
30436         /**
30437          * @event regionexpanded
30438          * Fires when a region is expanded.  
30439          * @param {Roo.LayoutRegion} region The expanded region
30440          */
30441         "regionexpanded" : true
30442     });
30443     this.updating = false;
30444     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
30445 };
30446
30447 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
30448     /**
30449      * Returns true if this layout is currently being updated
30450      * @return {Boolean}
30451      */
30452     isUpdating : function(){
30453         return this.updating; 
30454     },
30455     
30456     /**
30457      * Suspend the LayoutManager from doing auto-layouts while
30458      * making multiple add or remove calls
30459      */
30460     beginUpdate : function(){
30461         this.updating = true;    
30462     },
30463     
30464     /**
30465      * Restore auto-layouts and optionally disable the manager from performing a layout
30466      * @param {Boolean} noLayout true to disable a layout update 
30467      */
30468     endUpdate : function(noLayout){
30469         this.updating = false;
30470         if(!noLayout){
30471             this.layout();
30472         }    
30473     },
30474     
30475     layout: function(){
30476         
30477     },
30478     
30479     onRegionResized : function(region, newSize){
30480         this.fireEvent("regionresized", region, newSize);
30481         this.layout();
30482     },
30483     
30484     onRegionCollapsed : function(region){
30485         this.fireEvent("regioncollapsed", region);
30486     },
30487     
30488     onRegionExpanded : function(region){
30489         this.fireEvent("regionexpanded", region);
30490     },
30491         
30492     /**
30493      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
30494      * performs box-model adjustments.
30495      * @return {Object} The size as an object {width: (the width), height: (the height)}
30496      */
30497     getViewSize : function(){
30498         var size;
30499         if(this.el.dom != document.body){
30500             size = this.el.getSize();
30501         }else{
30502             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
30503         }
30504         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
30505         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30506         return size;
30507     },
30508     
30509     /**
30510      * Returns the Element this layout is bound to.
30511      * @return {Roo.Element}
30512      */
30513     getEl : function(){
30514         return this.el;
30515     },
30516     
30517     /**
30518      * Returns the specified region.
30519      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
30520      * @return {Roo.LayoutRegion}
30521      */
30522     getRegion : function(target){
30523         return this.regions[target.toLowerCase()];
30524     },
30525     
30526     onWindowResize : function(){
30527         if(this.monitorWindowResize){
30528             this.layout();
30529         }
30530     }
30531 });/*
30532  * Based on:
30533  * Ext JS Library 1.1.1
30534  * Copyright(c) 2006-2007, Ext JS, LLC.
30535  *
30536  * Originally Released Under LGPL - original licence link has changed is not relivant.
30537  *
30538  * Fork - LGPL
30539  * <script type="text/javascript">
30540  */
30541 /**
30542  * @class Roo.BorderLayout
30543  * @extends Roo.LayoutManager
30544  * @children Roo.ContentPanel
30545  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
30546  * please see: <br><br>
30547  * <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>
30548  * <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>
30549  * Example:
30550  <pre><code>
30551  var layout = new Roo.BorderLayout(document.body, {
30552     north: {
30553         initialSize: 25,
30554         titlebar: false
30555     },
30556     west: {
30557         split:true,
30558         initialSize: 200,
30559         minSize: 175,
30560         maxSize: 400,
30561         titlebar: true,
30562         collapsible: true
30563     },
30564     east: {
30565         split:true,
30566         initialSize: 202,
30567         minSize: 175,
30568         maxSize: 400,
30569         titlebar: true,
30570         collapsible: true
30571     },
30572     south: {
30573         split:true,
30574         initialSize: 100,
30575         minSize: 100,
30576         maxSize: 200,
30577         titlebar: true,
30578         collapsible: true
30579     },
30580     center: {
30581         titlebar: true,
30582         autoScroll:true,
30583         resizeTabs: true,
30584         minTabWidth: 50,
30585         preferredTabWidth: 150
30586     }
30587 });
30588
30589 // shorthand
30590 var CP = Roo.ContentPanel;
30591
30592 layout.beginUpdate();
30593 layout.add("north", new CP("north", "North"));
30594 layout.add("south", new CP("south", {title: "South", closable: true}));
30595 layout.add("west", new CP("west", {title: "West"}));
30596 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
30597 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
30598 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
30599 layout.getRegion("center").showPanel("center1");
30600 layout.endUpdate();
30601 </code></pre>
30602
30603 <b>The container the layout is rendered into can be either the body element or any other element.
30604 If it is not the body element, the container needs to either be an absolute positioned element,
30605 or you will need to add "position:relative" to the css of the container.  You will also need to specify
30606 the container size if it is not the body element.</b>
30607
30608 * @constructor
30609 * Create a new BorderLayout
30610 * @param {String/HTMLElement/Element} container The container this layout is bound to
30611 * @param {Object} config Configuration options
30612  */
30613 Roo.BorderLayout = function(container, config){
30614     config = config || {};
30615     Roo.BorderLayout.superclass.constructor.call(this, container, config);
30616     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
30617     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
30618         var target = this.factory.validRegions[i];
30619         if(config[target]){
30620             this.addRegion(target, config[target]);
30621         }
30622     }
30623 };
30624
30625 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
30626         
30627         /**
30628          * @cfg {Roo.LayoutRegion} east
30629          */
30630         /**
30631          * @cfg {Roo.LayoutRegion} west
30632          */
30633         /**
30634          * @cfg {Roo.LayoutRegion} north
30635          */
30636         /**
30637          * @cfg {Roo.LayoutRegion} south
30638          */
30639         /**
30640          * @cfg {Roo.LayoutRegion} center
30641          */
30642     /**
30643      * Creates and adds a new region if it doesn't already exist.
30644      * @param {String} target The target region key (north, south, east, west or center).
30645      * @param {Object} config The regions config object
30646      * @return {BorderLayoutRegion} The new region
30647      */
30648     addRegion : function(target, config){
30649         if(!this.regions[target]){
30650             var r = this.factory.create(target, this, config);
30651             this.bindRegion(target, r);
30652         }
30653         return this.regions[target];
30654     },
30655
30656     // private (kinda)
30657     bindRegion : function(name, r){
30658         this.regions[name] = r;
30659         r.on("visibilitychange", this.layout, this);
30660         r.on("paneladded", this.layout, this);
30661         r.on("panelremoved", this.layout, this);
30662         r.on("invalidated", this.layout, this);
30663         r.on("resized", this.onRegionResized, this);
30664         r.on("collapsed", this.onRegionCollapsed, this);
30665         r.on("expanded", this.onRegionExpanded, this);
30666     },
30667
30668     /**
30669      * Performs a layout update.
30670      */
30671     layout : function(){
30672         if(this.updating) {
30673             return;
30674         }
30675         var size = this.getViewSize();
30676         var w = size.width;
30677         var h = size.height;
30678         var centerW = w;
30679         var centerH = h;
30680         var centerY = 0;
30681         var centerX = 0;
30682         //var x = 0, y = 0;
30683
30684         var rs = this.regions;
30685         var north = rs["north"];
30686         var south = rs["south"]; 
30687         var west = rs["west"];
30688         var east = rs["east"];
30689         var center = rs["center"];
30690         //if(this.hideOnLayout){ // not supported anymore
30691             //c.el.setStyle("display", "none");
30692         //}
30693         if(north && north.isVisible()){
30694             var b = north.getBox();
30695             var m = north.getMargins();
30696             b.width = w - (m.left+m.right);
30697             b.x = m.left;
30698             b.y = m.top;
30699             centerY = b.height + b.y + m.bottom;
30700             centerH -= centerY;
30701             north.updateBox(this.safeBox(b));
30702         }
30703         if(south && south.isVisible()){
30704             var b = south.getBox();
30705             var m = south.getMargins();
30706             b.width = w - (m.left+m.right);
30707             b.x = m.left;
30708             var totalHeight = (b.height + m.top + m.bottom);
30709             b.y = h - totalHeight + m.top;
30710             centerH -= totalHeight;
30711             south.updateBox(this.safeBox(b));
30712         }
30713         if(west && west.isVisible()){
30714             var b = west.getBox();
30715             var m = west.getMargins();
30716             b.height = centerH - (m.top+m.bottom);
30717             b.x = m.left;
30718             b.y = centerY + m.top;
30719             var totalWidth = (b.width + m.left + m.right);
30720             centerX += totalWidth;
30721             centerW -= totalWidth;
30722             west.updateBox(this.safeBox(b));
30723         }
30724         if(east && east.isVisible()){
30725             var b = east.getBox();
30726             var m = east.getMargins();
30727             b.height = centerH - (m.top+m.bottom);
30728             var totalWidth = (b.width + m.left + m.right);
30729             b.x = w - totalWidth + m.left;
30730             b.y = centerY + m.top;
30731             centerW -= totalWidth;
30732             east.updateBox(this.safeBox(b));
30733         }
30734         if(center){
30735             var m = center.getMargins();
30736             var centerBox = {
30737                 x: centerX + m.left,
30738                 y: centerY + m.top,
30739                 width: centerW - (m.left+m.right),
30740                 height: centerH - (m.top+m.bottom)
30741             };
30742             //if(this.hideOnLayout){
30743                 //center.el.setStyle("display", "block");
30744             //}
30745             center.updateBox(this.safeBox(centerBox));
30746         }
30747         this.el.repaint();
30748         this.fireEvent("layout", this);
30749     },
30750
30751     // private
30752     safeBox : function(box){
30753         box.width = Math.max(0, box.width);
30754         box.height = Math.max(0, box.height);
30755         return box;
30756     },
30757
30758     /**
30759      * Adds a ContentPanel (or subclass) to this layout.
30760      * @param {String} target The target region key (north, south, east, west or center).
30761      * @param {Roo.ContentPanel} panel The panel to add
30762      * @return {Roo.ContentPanel} The added panel
30763      */
30764     add : function(target, panel){
30765          
30766         target = target.toLowerCase();
30767         return this.regions[target].add(panel);
30768     },
30769
30770     /**
30771      * Remove a ContentPanel (or subclass) to this layout.
30772      * @param {String} target The target region key (north, south, east, west or center).
30773      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
30774      * @return {Roo.ContentPanel} The removed panel
30775      */
30776     remove : function(target, panel){
30777         target = target.toLowerCase();
30778         return this.regions[target].remove(panel);
30779     },
30780
30781     /**
30782      * Searches all regions for a panel with the specified id
30783      * @param {String} panelId
30784      * @return {Roo.ContentPanel} The panel or null if it wasn't found
30785      */
30786     findPanel : function(panelId){
30787         var rs = this.regions;
30788         for(var target in rs){
30789             if(typeof rs[target] != "function"){
30790                 var p = rs[target].getPanel(panelId);
30791                 if(p){
30792                     return p;
30793                 }
30794             }
30795         }
30796         return null;
30797     },
30798
30799     /**
30800      * Searches all regions for a panel with the specified id and activates (shows) it.
30801      * @param {String/ContentPanel} panelId The panels id or the panel itself
30802      * @return {Roo.ContentPanel} The shown panel or null
30803      */
30804     showPanel : function(panelId) {
30805       var rs = this.regions;
30806       for(var target in rs){
30807          var r = rs[target];
30808          if(typeof r != "function"){
30809             if(r.hasPanel(panelId)){
30810                return r.showPanel(panelId);
30811             }
30812          }
30813       }
30814       return null;
30815    },
30816
30817    /**
30818      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
30819      * @param {Roo.state.Provider} provider (optional) An alternate state provider
30820      */
30821     restoreState : function(provider){
30822         if(!provider){
30823             provider = Roo.state.Manager;
30824         }
30825         var sm = new Roo.LayoutStateManager();
30826         sm.init(this, provider);
30827     },
30828
30829     /**
30830      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
30831      * object should contain properties for each region to add ContentPanels to, and each property's value should be
30832      * a valid ContentPanel config object.  Example:
30833      * <pre><code>
30834 // Create the main layout
30835 var layout = new Roo.BorderLayout('main-ct', {
30836     west: {
30837         split:true,
30838         minSize: 175,
30839         titlebar: true
30840     },
30841     center: {
30842         title:'Components'
30843     }
30844 }, 'main-ct');
30845
30846 // Create and add multiple ContentPanels at once via configs
30847 layout.batchAdd({
30848    west: {
30849        id: 'source-files',
30850        autoCreate:true,
30851        title:'Ext Source Files',
30852        autoScroll:true,
30853        fitToFrame:true
30854    },
30855    center : {
30856        el: cview,
30857        autoScroll:true,
30858        fitToFrame:true,
30859        toolbar: tb,
30860        resizeEl:'cbody'
30861    }
30862 });
30863 </code></pre>
30864      * @param {Object} regions An object containing ContentPanel configs by region name
30865      */
30866     batchAdd : function(regions){
30867         this.beginUpdate();
30868         for(var rname in regions){
30869             var lr = this.regions[rname];
30870             if(lr){
30871                 this.addTypedPanels(lr, regions[rname]);
30872             }
30873         }
30874         this.endUpdate();
30875     },
30876
30877     // private
30878     addTypedPanels : function(lr, ps){
30879         if(typeof ps == 'string'){
30880             lr.add(new Roo.ContentPanel(ps));
30881         }
30882         else if(ps instanceof Array){
30883             for(var i =0, len = ps.length; i < len; i++){
30884                 this.addTypedPanels(lr, ps[i]);
30885             }
30886         }
30887         else if(!ps.events){ // raw config?
30888             var el = ps.el;
30889             delete ps.el; // prevent conflict
30890             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
30891         }
30892         else {  // panel object assumed!
30893             lr.add(ps);
30894         }
30895     },
30896     /**
30897      * Adds a xtype elements to the layout.
30898      * <pre><code>
30899
30900 layout.addxtype({
30901        xtype : 'ContentPanel',
30902        region: 'west',
30903        items: [ .... ]
30904    }
30905 );
30906
30907 layout.addxtype({
30908         xtype : 'NestedLayoutPanel',
30909         region: 'west',
30910         layout: {
30911            center: { },
30912            west: { }   
30913         },
30914         items : [ ... list of content panels or nested layout panels.. ]
30915    }
30916 );
30917 </code></pre>
30918      * @param {Object} cfg Xtype definition of item to add.
30919      */
30920     addxtype : function(cfg)
30921     {
30922         // basically accepts a pannel...
30923         // can accept a layout region..!?!?
30924         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
30925         
30926         if (!cfg.xtype.match(/Panel$/)) {
30927             return false;
30928         }
30929         var ret = false;
30930         
30931         if (typeof(cfg.region) == 'undefined') {
30932             Roo.log("Failed to add Panel, region was not set");
30933             Roo.log(cfg);
30934             return false;
30935         }
30936         var region = cfg.region;
30937         delete cfg.region;
30938         
30939           
30940         var xitems = [];
30941         if (cfg.items) {
30942             xitems = cfg.items;
30943             delete cfg.items;
30944         }
30945         var nb = false;
30946         
30947         switch(cfg.xtype) 
30948         {
30949             case 'ContentPanel':  // ContentPanel (el, cfg)
30950             case 'ScrollPanel':  // ContentPanel (el, cfg)
30951             case 'ViewPanel': 
30952                 if(cfg.autoCreate) {
30953                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30954                 } else {
30955                     var el = this.el.createChild();
30956                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
30957                 }
30958                 
30959                 this.add(region, ret);
30960                 break;
30961             
30962             
30963             case 'TreePanel': // our new panel!
30964                 cfg.el = this.el.createChild();
30965                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30966                 this.add(region, ret);
30967                 break;
30968             
30969             case 'NestedLayoutPanel': 
30970                 // create a new Layout (which is  a Border Layout...
30971                 var el = this.el.createChild();
30972                 var clayout = cfg.layout;
30973                 delete cfg.layout;
30974                 clayout.items   = clayout.items  || [];
30975                 // replace this exitems with the clayout ones..
30976                 xitems = clayout.items;
30977                  
30978                 
30979                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
30980                     cfg.background = false;
30981                 }
30982                 var layout = new Roo.BorderLayout(el, clayout);
30983                 
30984                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
30985                 //console.log('adding nested layout panel '  + cfg.toSource());
30986                 this.add(region, ret);
30987                 nb = {}; /// find first...
30988                 break;
30989                 
30990             case 'GridPanel': 
30991             
30992                 // needs grid and region
30993                 
30994                 //var el = this.getRegion(region).el.createChild();
30995                 var el = this.el.createChild();
30996                 // create the grid first...
30997                 
30998                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
30999                 delete cfg.grid;
31000                 if (region == 'center' && this.active ) {
31001                     cfg.background = false;
31002                 }
31003                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
31004                 
31005                 this.add(region, ret);
31006                 if (cfg.background) {
31007                     ret.on('activate', function(gp) {
31008                         if (!gp.grid.rendered) {
31009                             gp.grid.render();
31010                         }
31011                     });
31012                 } else {
31013                     grid.render();
31014                 }
31015                 break;
31016            
31017            
31018            
31019                 
31020                 
31021                 
31022             default:
31023                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
31024                     
31025                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31026                     this.add(region, ret);
31027                 } else {
31028                 
31029                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
31030                     return null;
31031                 }
31032                 
31033              // GridPanel (grid, cfg)
31034             
31035         }
31036         this.beginUpdate();
31037         // add children..
31038         var region = '';
31039         var abn = {};
31040         Roo.each(xitems, function(i)  {
31041             region = nb && i.region ? i.region : false;
31042             
31043             var add = ret.addxtype(i);
31044            
31045             if (region) {
31046                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31047                 if (!i.background) {
31048                     abn[region] = nb[region] ;
31049                 }
31050             }
31051             
31052         });
31053         this.endUpdate();
31054
31055         // make the last non-background panel active..
31056         //if (nb) { Roo.log(abn); }
31057         if (nb) {
31058             
31059             for(var r in abn) {
31060                 region = this.getRegion(r);
31061                 if (region) {
31062                     // tried using nb[r], but it does not work..
31063                      
31064                     region.showPanel(abn[r]);
31065                    
31066                 }
31067             }
31068         }
31069         return ret;
31070         
31071     }
31072 });
31073
31074 /**
31075  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
31076  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
31077  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
31078  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
31079  * <pre><code>
31080 // shorthand
31081 var CP = Roo.ContentPanel;
31082
31083 var layout = Roo.BorderLayout.create({
31084     north: {
31085         initialSize: 25,
31086         titlebar: false,
31087         panels: [new CP("north", "North")]
31088     },
31089     west: {
31090         split:true,
31091         initialSize: 200,
31092         minSize: 175,
31093         maxSize: 400,
31094         titlebar: true,
31095         collapsible: true,
31096         panels: [new CP("west", {title: "West"})]
31097     },
31098     east: {
31099         split:true,
31100         initialSize: 202,
31101         minSize: 175,
31102         maxSize: 400,
31103         titlebar: true,
31104         collapsible: true,
31105         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
31106     },
31107     south: {
31108         split:true,
31109         initialSize: 100,
31110         minSize: 100,
31111         maxSize: 200,
31112         titlebar: true,
31113         collapsible: true,
31114         panels: [new CP("south", {title: "South", closable: true})]
31115     },
31116     center: {
31117         titlebar: true,
31118         autoScroll:true,
31119         resizeTabs: true,
31120         minTabWidth: 50,
31121         preferredTabWidth: 150,
31122         panels: [
31123             new CP("center1", {title: "Close Me", closable: true}),
31124             new CP("center2", {title: "Center Panel", closable: false})
31125         ]
31126     }
31127 }, document.body);
31128
31129 layout.getRegion("center").showPanel("center1");
31130 </code></pre>
31131  * @param config
31132  * @param targetEl
31133  */
31134 Roo.BorderLayout.create = function(config, targetEl){
31135     var layout = new Roo.BorderLayout(targetEl || document.body, config);
31136     layout.beginUpdate();
31137     var regions = Roo.BorderLayout.RegionFactory.validRegions;
31138     for(var j = 0, jlen = regions.length; j < jlen; j++){
31139         var lr = regions[j];
31140         if(layout.regions[lr] && config[lr].panels){
31141             var r = layout.regions[lr];
31142             var ps = config[lr].panels;
31143             layout.addTypedPanels(r, ps);
31144         }
31145     }
31146     layout.endUpdate();
31147     return layout;
31148 };
31149
31150 // private
31151 Roo.BorderLayout.RegionFactory = {
31152     // private
31153     validRegions : ["north","south","east","west","center"],
31154
31155     // private
31156     create : function(target, mgr, config){
31157         target = target.toLowerCase();
31158         if(config.lightweight || config.basic){
31159             return new Roo.BasicLayoutRegion(mgr, config, target);
31160         }
31161         switch(target){
31162             case "north":
31163                 return new Roo.NorthLayoutRegion(mgr, config);
31164             case "south":
31165                 return new Roo.SouthLayoutRegion(mgr, config);
31166             case "east":
31167                 return new Roo.EastLayoutRegion(mgr, config);
31168             case "west":
31169                 return new Roo.WestLayoutRegion(mgr, config);
31170             case "center":
31171                 return new Roo.CenterLayoutRegion(mgr, config);
31172         }
31173         throw 'Layout region "'+target+'" not supported.';
31174     }
31175 };/*
31176  * Based on:
31177  * Ext JS Library 1.1.1
31178  * Copyright(c) 2006-2007, Ext JS, LLC.
31179  *
31180  * Originally Released Under LGPL - original licence link has changed is not relivant.
31181  *
31182  * Fork - LGPL
31183  * <script type="text/javascript">
31184  */
31185  
31186 /**
31187  * @class Roo.BasicLayoutRegion
31188  * @extends Roo.util.Observable
31189  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31190  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31191  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31192  */
31193 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
31194     this.mgr = mgr;
31195     this.position  = pos;
31196     this.events = {
31197         /**
31198          * @scope Roo.BasicLayoutRegion
31199          */
31200         
31201         /**
31202          * @event beforeremove
31203          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31204          * @param {Roo.LayoutRegion} this
31205          * @param {Roo.ContentPanel} panel The panel
31206          * @param {Object} e The cancel event object
31207          */
31208         "beforeremove" : true,
31209         /**
31210          * @event invalidated
31211          * Fires when the layout for this region is changed.
31212          * @param {Roo.LayoutRegion} this
31213          */
31214         "invalidated" : true,
31215         /**
31216          * @event visibilitychange
31217          * Fires when this region is shown or hidden 
31218          * @param {Roo.LayoutRegion} this
31219          * @param {Boolean} visibility true or false
31220          */
31221         "visibilitychange" : true,
31222         /**
31223          * @event paneladded
31224          * Fires when a panel is added. 
31225          * @param {Roo.LayoutRegion} this
31226          * @param {Roo.ContentPanel} panel The panel
31227          */
31228         "paneladded" : true,
31229         /**
31230          * @event panelremoved
31231          * Fires when a panel is removed. 
31232          * @param {Roo.LayoutRegion} this
31233          * @param {Roo.ContentPanel} panel The panel
31234          */
31235         "panelremoved" : true,
31236         /**
31237          * @event beforecollapse
31238          * Fires when this region before collapse.
31239          * @param {Roo.LayoutRegion} this
31240          */
31241         "beforecollapse" : true,
31242         /**
31243          * @event collapsed
31244          * Fires when this region is collapsed.
31245          * @param {Roo.LayoutRegion} this
31246          */
31247         "collapsed" : true,
31248         /**
31249          * @event expanded
31250          * Fires when this region is expanded.
31251          * @param {Roo.LayoutRegion} this
31252          */
31253         "expanded" : true,
31254         /**
31255          * @event slideshow
31256          * Fires when this region is slid into view.
31257          * @param {Roo.LayoutRegion} this
31258          */
31259         "slideshow" : true,
31260         /**
31261          * @event slidehide
31262          * Fires when this region slides out of view. 
31263          * @param {Roo.LayoutRegion} this
31264          */
31265         "slidehide" : true,
31266         /**
31267          * @event panelactivated
31268          * Fires when a panel is activated. 
31269          * @param {Roo.LayoutRegion} this
31270          * @param {Roo.ContentPanel} panel The activated panel
31271          */
31272         "panelactivated" : true,
31273         /**
31274          * @event resized
31275          * Fires when the user resizes this region. 
31276          * @param {Roo.LayoutRegion} this
31277          * @param {Number} newSize The new size (width for east/west, height for north/south)
31278          */
31279         "resized" : true
31280     };
31281     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31282     this.panels = new Roo.util.MixedCollection();
31283     this.panels.getKey = this.getPanelId.createDelegate(this);
31284     this.box = null;
31285     this.activePanel = null;
31286     // ensure listeners are added...
31287     
31288     if (config.listeners || config.events) {
31289         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
31290             listeners : config.listeners || {},
31291             events : config.events || {}
31292         });
31293     }
31294     
31295     if(skipConfig !== true){
31296         this.applyConfig(config);
31297     }
31298 };
31299
31300 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
31301     getPanelId : function(p){
31302         return p.getId();
31303     },
31304     
31305     applyConfig : function(config){
31306         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31307         this.config = config;
31308         
31309     },
31310     
31311     /**
31312      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31313      * the width, for horizontal (north, south) the height.
31314      * @param {Number} newSize The new width or height
31315      */
31316     resizeTo : function(newSize){
31317         var el = this.el ? this.el :
31318                  (this.activePanel ? this.activePanel.getEl() : null);
31319         if(el){
31320             switch(this.position){
31321                 case "east":
31322                 case "west":
31323                     el.setWidth(newSize);
31324                     this.fireEvent("resized", this, newSize);
31325                 break;
31326                 case "north":
31327                 case "south":
31328                     el.setHeight(newSize);
31329                     this.fireEvent("resized", this, newSize);
31330                 break;                
31331             }
31332         }
31333     },
31334     
31335     getBox : function(){
31336         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31337     },
31338     
31339     getMargins : function(){
31340         return this.margins;
31341     },
31342     
31343     updateBox : function(box){
31344         this.box = box;
31345         var el = this.activePanel.getEl();
31346         el.dom.style.left = box.x + "px";
31347         el.dom.style.top = box.y + "px";
31348         this.activePanel.setSize(box.width, box.height);
31349     },
31350     
31351     /**
31352      * Returns the container element for this region.
31353      * @return {Roo.Element}
31354      */
31355     getEl : function(){
31356         return this.activePanel;
31357     },
31358     
31359     /**
31360      * Returns true if this region is currently visible.
31361      * @return {Boolean}
31362      */
31363     isVisible : function(){
31364         return this.activePanel ? true : false;
31365     },
31366     
31367     setActivePanel : function(panel){
31368         panel = this.getPanel(panel);
31369         if(this.activePanel && this.activePanel != panel){
31370             this.activePanel.setActiveState(false);
31371             this.activePanel.getEl().setLeftTop(-10000,-10000);
31372         }
31373         this.activePanel = panel;
31374         panel.setActiveState(true);
31375         if(this.box){
31376             panel.setSize(this.box.width, this.box.height);
31377         }
31378         this.fireEvent("panelactivated", this, panel);
31379         this.fireEvent("invalidated");
31380     },
31381     
31382     /**
31383      * Show the specified panel.
31384      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31385      * @return {Roo.ContentPanel} The shown panel or null
31386      */
31387     showPanel : function(panel){
31388         if(panel = this.getPanel(panel)){
31389             this.setActivePanel(panel);
31390         }
31391         return panel;
31392     },
31393     
31394     /**
31395      * Get the active panel for this region.
31396      * @return {Roo.ContentPanel} The active panel or null
31397      */
31398     getActivePanel : function(){
31399         return this.activePanel;
31400     },
31401     
31402     /**
31403      * Add the passed ContentPanel(s)
31404      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31405      * @return {Roo.ContentPanel} The panel added (if only one was added)
31406      */
31407     add : function(panel){
31408         if(arguments.length > 1){
31409             for(var i = 0, len = arguments.length; i < len; i++) {
31410                 this.add(arguments[i]);
31411             }
31412             return null;
31413         }
31414         if(this.hasPanel(panel)){
31415             this.showPanel(panel);
31416             return panel;
31417         }
31418         var el = panel.getEl();
31419         if(el.dom.parentNode != this.mgr.el.dom){
31420             this.mgr.el.dom.appendChild(el.dom);
31421         }
31422         if(panel.setRegion){
31423             panel.setRegion(this);
31424         }
31425         this.panels.add(panel);
31426         el.setStyle("position", "absolute");
31427         if(!panel.background){
31428             this.setActivePanel(panel);
31429             if(this.config.initialSize && this.panels.getCount()==1){
31430                 this.resizeTo(this.config.initialSize);
31431             }
31432         }
31433         this.fireEvent("paneladded", this, panel);
31434         return panel;
31435     },
31436     
31437     /**
31438      * Returns true if the panel is in this region.
31439      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31440      * @return {Boolean}
31441      */
31442     hasPanel : function(panel){
31443         if(typeof panel == "object"){ // must be panel obj
31444             panel = panel.getId();
31445         }
31446         return this.getPanel(panel) ? true : false;
31447     },
31448     
31449     /**
31450      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31451      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31452      * @param {Boolean} preservePanel Overrides the config preservePanel option
31453      * @return {Roo.ContentPanel} The panel that was removed
31454      */
31455     remove : function(panel, preservePanel){
31456         panel = this.getPanel(panel);
31457         if(!panel){
31458             return null;
31459         }
31460         var e = {};
31461         this.fireEvent("beforeremove", this, panel, e);
31462         if(e.cancel === true){
31463             return null;
31464         }
31465         var panelId = panel.getId();
31466         this.panels.removeKey(panelId);
31467         return panel;
31468     },
31469     
31470     /**
31471      * Returns the panel specified or null if it's not in this region.
31472      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31473      * @return {Roo.ContentPanel}
31474      */
31475     getPanel : function(id){
31476         if(typeof id == "object"){ // must be panel obj
31477             return id;
31478         }
31479         return this.panels.get(id);
31480     },
31481     
31482     /**
31483      * Returns this regions position (north/south/east/west/center).
31484      * @return {String} 
31485      */
31486     getPosition: function(){
31487         return this.position;    
31488     }
31489 });/*
31490  * Based on:
31491  * Ext JS Library 1.1.1
31492  * Copyright(c) 2006-2007, Ext JS, LLC.
31493  *
31494  * Originally Released Under LGPL - original licence link has changed is not relivant.
31495  *
31496  * Fork - LGPL
31497  * <script type="text/javascript">
31498  */
31499  
31500 /**
31501  * @class Roo.LayoutRegion
31502  * @extends Roo.BasicLayoutRegion
31503  * This class represents a region in a layout manager.
31504  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
31505  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
31506  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
31507  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
31508  * @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})
31509  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
31510  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
31511  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
31512  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
31513  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
31514  * @cfg {String}    title           The title for the region (overrides panel titles)
31515  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
31516  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
31517  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
31518  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
31519  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
31520  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
31521  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
31522  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
31523  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
31524  * @cfg {Boolean}   showPin         True to show a pin button
31525  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
31526  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
31527  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
31528  * @cfg {Number}    width           For East/West panels
31529  * @cfg {Number}    height          For North/South panels
31530  * @cfg {Boolean}   split           To show the splitter
31531  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
31532  */
31533 Roo.LayoutRegion = function(mgr, config, pos){
31534     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
31535     var dh = Roo.DomHelper;
31536     /** This region's container element 
31537     * @type Roo.Element */
31538     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
31539     /** This region's title element 
31540     * @type Roo.Element */
31541
31542     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
31543         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
31544         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
31545     ]}, true);
31546     this.titleEl.enableDisplayMode();
31547     /** This region's title text element 
31548     * @type HTMLElement */
31549     this.titleTextEl = this.titleEl.dom.firstChild;
31550     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
31551     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
31552     this.closeBtn.enableDisplayMode();
31553     this.closeBtn.on("click", this.closeClicked, this);
31554     this.closeBtn.hide();
31555
31556     this.createBody(config);
31557     this.visible = true;
31558     this.collapsed = false;
31559
31560     if(config.hideWhenEmpty){
31561         this.hide();
31562         this.on("paneladded", this.validateVisibility, this);
31563         this.on("panelremoved", this.validateVisibility, this);
31564     }
31565     this.applyConfig(config);
31566 };
31567
31568 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
31569
31570     createBody : function(){
31571         /** This region's body element 
31572         * @type Roo.Element */
31573         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
31574     },
31575
31576     applyConfig : function(c){
31577         if(c.collapsible && this.position != "center" && !this.collapsedEl){
31578             var dh = Roo.DomHelper;
31579             if(c.titlebar !== false){
31580                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
31581                 this.collapseBtn.on("click", this.collapse, this);
31582                 this.collapseBtn.enableDisplayMode();
31583
31584                 if(c.showPin === true || this.showPin){
31585                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
31586                     this.stickBtn.enableDisplayMode();
31587                     this.stickBtn.on("click", this.expand, this);
31588                     this.stickBtn.hide();
31589                 }
31590             }
31591             /** This region's collapsed element
31592             * @type Roo.Element */
31593             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
31594                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
31595             ]}, true);
31596             if(c.floatable !== false){
31597                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
31598                this.collapsedEl.on("click", this.collapseClick, this);
31599             }
31600
31601             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
31602                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
31603                    id: "message", unselectable: "on", style:{"float":"left"}});
31604                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
31605              }
31606             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
31607             this.expandBtn.on("click", this.expand, this);
31608         }
31609         if(this.collapseBtn){
31610             this.collapseBtn.setVisible(c.collapsible == true);
31611         }
31612         this.cmargins = c.cmargins || this.cmargins ||
31613                          (this.position == "west" || this.position == "east" ?
31614                              {top: 0, left: 2, right:2, bottom: 0} :
31615                              {top: 2, left: 0, right:0, bottom: 2});
31616         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31617         this.bottomTabs = c.tabPosition != "top";
31618         this.autoScroll = c.autoScroll || false;
31619         if(this.autoScroll){
31620             this.bodyEl.setStyle("overflow", "auto");
31621         }else{
31622             this.bodyEl.setStyle("overflow", "hidden");
31623         }
31624         //if(c.titlebar !== false){
31625             if((!c.titlebar && !c.title) || c.titlebar === false){
31626                 this.titleEl.hide();
31627             }else{
31628                 this.titleEl.show();
31629                 if(c.title){
31630                     this.titleTextEl.innerHTML = c.title;
31631                 }
31632             }
31633         //}
31634         this.duration = c.duration || .30;
31635         this.slideDuration = c.slideDuration || .45;
31636         this.config = c;
31637         if(c.collapsed){
31638             this.collapse(true);
31639         }
31640         if(c.hidden){
31641             this.hide();
31642         }
31643     },
31644     /**
31645      * Returns true if this region is currently visible.
31646      * @return {Boolean}
31647      */
31648     isVisible : function(){
31649         return this.visible;
31650     },
31651
31652     /**
31653      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
31654      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
31655      */
31656     setCollapsedTitle : function(title){
31657         title = title || "&#160;";
31658         if(this.collapsedTitleTextEl){
31659             this.collapsedTitleTextEl.innerHTML = title;
31660         }
31661     },
31662
31663     getBox : function(){
31664         var b;
31665         if(!this.collapsed){
31666             b = this.el.getBox(false, true);
31667         }else{
31668             b = this.collapsedEl.getBox(false, true);
31669         }
31670         return b;
31671     },
31672
31673     getMargins : function(){
31674         return this.collapsed ? this.cmargins : this.margins;
31675     },
31676
31677     highlight : function(){
31678         this.el.addClass("x-layout-panel-dragover");
31679     },
31680
31681     unhighlight : function(){
31682         this.el.removeClass("x-layout-panel-dragover");
31683     },
31684
31685     updateBox : function(box){
31686         this.box = box;
31687         if(!this.collapsed){
31688             this.el.dom.style.left = box.x + "px";
31689             this.el.dom.style.top = box.y + "px";
31690             this.updateBody(box.width, box.height);
31691         }else{
31692             this.collapsedEl.dom.style.left = box.x + "px";
31693             this.collapsedEl.dom.style.top = box.y + "px";
31694             this.collapsedEl.setSize(box.width, box.height);
31695         }
31696         if(this.tabs){
31697             this.tabs.autoSizeTabs();
31698         }
31699     },
31700
31701     updateBody : function(w, h){
31702         if(w !== null){
31703             this.el.setWidth(w);
31704             w -= this.el.getBorderWidth("rl");
31705             if(this.config.adjustments){
31706                 w += this.config.adjustments[0];
31707             }
31708         }
31709         if(h !== null){
31710             this.el.setHeight(h);
31711             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
31712             h -= this.el.getBorderWidth("tb");
31713             if(this.config.adjustments){
31714                 h += this.config.adjustments[1];
31715             }
31716             this.bodyEl.setHeight(h);
31717             if(this.tabs){
31718                 h = this.tabs.syncHeight(h);
31719             }
31720         }
31721         if(this.panelSize){
31722             w = w !== null ? w : this.panelSize.width;
31723             h = h !== null ? h : this.panelSize.height;
31724         }
31725         if(this.activePanel){
31726             var el = this.activePanel.getEl();
31727             w = w !== null ? w : el.getWidth();
31728             h = h !== null ? h : el.getHeight();
31729             this.panelSize = {width: w, height: h};
31730             this.activePanel.setSize(w, h);
31731         }
31732         if(Roo.isIE && this.tabs){
31733             this.tabs.el.repaint();
31734         }
31735     },
31736
31737     /**
31738      * Returns the container element for this region.
31739      * @return {Roo.Element}
31740      */
31741     getEl : function(){
31742         return this.el;
31743     },
31744
31745     /**
31746      * Hides this region.
31747      */
31748     hide : function(){
31749         if(!this.collapsed){
31750             this.el.dom.style.left = "-2000px";
31751             this.el.hide();
31752         }else{
31753             this.collapsedEl.dom.style.left = "-2000px";
31754             this.collapsedEl.hide();
31755         }
31756         this.visible = false;
31757         this.fireEvent("visibilitychange", this, false);
31758     },
31759
31760     /**
31761      * Shows this region if it was previously hidden.
31762      */
31763     show : function(){
31764         if(!this.collapsed){
31765             this.el.show();
31766         }else{
31767             this.collapsedEl.show();
31768         }
31769         this.visible = true;
31770         this.fireEvent("visibilitychange", this, true);
31771     },
31772
31773     closeClicked : function(){
31774         if(this.activePanel){
31775             this.remove(this.activePanel);
31776         }
31777     },
31778
31779     collapseClick : function(e){
31780         if(this.isSlid){
31781            e.stopPropagation();
31782            this.slideIn();
31783         }else{
31784            e.stopPropagation();
31785            this.slideOut();
31786         }
31787     },
31788
31789     /**
31790      * Collapses this region.
31791      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
31792      */
31793     collapse : function(skipAnim, skipCheck){
31794         if(this.collapsed) {
31795             return;
31796         }
31797         
31798         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
31799             
31800             this.collapsed = true;
31801             if(this.split){
31802                 this.split.el.hide();
31803             }
31804             if(this.config.animate && skipAnim !== true){
31805                 this.fireEvent("invalidated", this);
31806                 this.animateCollapse();
31807             }else{
31808                 this.el.setLocation(-20000,-20000);
31809                 this.el.hide();
31810                 this.collapsedEl.show();
31811                 this.fireEvent("collapsed", this);
31812                 this.fireEvent("invalidated", this);
31813             }
31814         }
31815         
31816     },
31817
31818     animateCollapse : function(){
31819         // overridden
31820     },
31821
31822     /**
31823      * Expands this region if it was previously collapsed.
31824      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
31825      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
31826      */
31827     expand : function(e, skipAnim){
31828         if(e) {
31829             e.stopPropagation();
31830         }
31831         if(!this.collapsed || this.el.hasActiveFx()) {
31832             return;
31833         }
31834         if(this.isSlid){
31835             this.afterSlideIn();
31836             skipAnim = true;
31837         }
31838         this.collapsed = false;
31839         if(this.config.animate && skipAnim !== true){
31840             this.animateExpand();
31841         }else{
31842             this.el.show();
31843             if(this.split){
31844                 this.split.el.show();
31845             }
31846             this.collapsedEl.setLocation(-2000,-2000);
31847             this.collapsedEl.hide();
31848             this.fireEvent("invalidated", this);
31849             this.fireEvent("expanded", this);
31850         }
31851     },
31852
31853     animateExpand : function(){
31854         // overridden
31855     },
31856
31857     initTabs : function()
31858     {
31859         this.bodyEl.setStyle("overflow", "hidden");
31860         var ts = new Roo.TabPanel(
31861                 this.bodyEl.dom,
31862                 {
31863                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
31864                     disableTooltips: this.config.disableTabTips,
31865                     toolbar : this.config.toolbar
31866                 }
31867         );
31868         if(this.config.hideTabs){
31869             ts.stripWrap.setDisplayed(false);
31870         }
31871         this.tabs = ts;
31872         ts.resizeTabs = this.config.resizeTabs === true;
31873         ts.minTabWidth = this.config.minTabWidth || 40;
31874         ts.maxTabWidth = this.config.maxTabWidth || 250;
31875         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
31876         ts.monitorResize = false;
31877         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31878         ts.bodyEl.addClass('x-layout-tabs-body');
31879         this.panels.each(this.initPanelAsTab, this);
31880     },
31881
31882     initPanelAsTab : function(panel){
31883         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
31884                     this.config.closeOnTab && panel.isClosable());
31885         if(panel.tabTip !== undefined){
31886             ti.setTooltip(panel.tabTip);
31887         }
31888         ti.on("activate", function(){
31889               this.setActivePanel(panel);
31890         }, this);
31891         if(this.config.closeOnTab){
31892             ti.on("beforeclose", function(t, e){
31893                 e.cancel = true;
31894                 this.remove(panel);
31895             }, this);
31896         }
31897         return ti;
31898     },
31899
31900     updatePanelTitle : function(panel, title){
31901         if(this.activePanel == panel){
31902             this.updateTitle(title);
31903         }
31904         if(this.tabs){
31905             var ti = this.tabs.getTab(panel.getEl().id);
31906             ti.setText(title);
31907             if(panel.tabTip !== undefined){
31908                 ti.setTooltip(panel.tabTip);
31909             }
31910         }
31911     },
31912
31913     updateTitle : function(title){
31914         if(this.titleTextEl && !this.config.title){
31915             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
31916         }
31917     },
31918
31919     setActivePanel : function(panel){
31920         panel = this.getPanel(panel);
31921         if(this.activePanel && this.activePanel != panel){
31922             this.activePanel.setActiveState(false);
31923         }
31924         this.activePanel = panel;
31925         panel.setActiveState(true);
31926         if(this.panelSize){
31927             panel.setSize(this.panelSize.width, this.panelSize.height);
31928         }
31929         if(this.closeBtn){
31930             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
31931         }
31932         this.updateTitle(panel.getTitle());
31933         if(this.tabs){
31934             this.fireEvent("invalidated", this);
31935         }
31936         this.fireEvent("panelactivated", this, panel);
31937     },
31938
31939     /**
31940      * Shows the specified panel.
31941      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
31942      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
31943      */
31944     showPanel : function(panel)
31945     {
31946         panel = this.getPanel(panel);
31947         if(panel){
31948             if(this.tabs){
31949                 var tab = this.tabs.getTab(panel.getEl().id);
31950                 if(tab.isHidden()){
31951                     this.tabs.unhideTab(tab.id);
31952                 }
31953                 tab.activate();
31954             }else{
31955                 this.setActivePanel(panel);
31956             }
31957         }
31958         return panel;
31959     },
31960
31961     /**
31962      * Get the active panel for this region.
31963      * @return {Roo.ContentPanel} The active panel or null
31964      */
31965     getActivePanel : function(){
31966         return this.activePanel;
31967     },
31968
31969     validateVisibility : function(){
31970         if(this.panels.getCount() < 1){
31971             this.updateTitle("&#160;");
31972             this.closeBtn.hide();
31973             this.hide();
31974         }else{
31975             if(!this.isVisible()){
31976                 this.show();
31977             }
31978         }
31979     },
31980
31981     /**
31982      * Adds the passed ContentPanel(s) to this region.
31983      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31984      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
31985      */
31986     add : function(panel){
31987         if(arguments.length > 1){
31988             for(var i = 0, len = arguments.length; i < len; i++) {
31989                 this.add(arguments[i]);
31990             }
31991             return null;
31992         }
31993         if(this.hasPanel(panel)){
31994             this.showPanel(panel);
31995             return panel;
31996         }
31997         panel.setRegion(this);
31998         this.panels.add(panel);
31999         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32000             this.bodyEl.dom.appendChild(panel.getEl().dom);
32001             if(panel.background !== true){
32002                 this.setActivePanel(panel);
32003             }
32004             this.fireEvent("paneladded", this, panel);
32005             return panel;
32006         }
32007         if(!this.tabs){
32008             this.initTabs();
32009         }else{
32010             this.initPanelAsTab(panel);
32011         }
32012         if(panel.background !== true){
32013             this.tabs.activate(panel.getEl().id);
32014         }
32015         this.fireEvent("paneladded", this, panel);
32016         return panel;
32017     },
32018
32019     /**
32020      * Hides the tab for the specified panel.
32021      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32022      */
32023     hidePanel : function(panel){
32024         if(this.tabs && (panel = this.getPanel(panel))){
32025             this.tabs.hideTab(panel.getEl().id);
32026         }
32027     },
32028
32029     /**
32030      * Unhides the tab for a previously hidden panel.
32031      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32032      */
32033     unhidePanel : function(panel){
32034         if(this.tabs && (panel = this.getPanel(panel))){
32035             this.tabs.unhideTab(panel.getEl().id);
32036         }
32037     },
32038
32039     clearPanels : function(){
32040         while(this.panels.getCount() > 0){
32041              this.remove(this.panels.first());
32042         }
32043     },
32044
32045     /**
32046      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32047      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32048      * @param {Boolean} preservePanel Overrides the config preservePanel option
32049      * @return {Roo.ContentPanel} The panel that was removed
32050      */
32051     remove : function(panel, preservePanel){
32052         panel = this.getPanel(panel);
32053         if(!panel){
32054             return null;
32055         }
32056         var e = {};
32057         this.fireEvent("beforeremove", this, panel, e);
32058         if(e.cancel === true){
32059             return null;
32060         }
32061         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32062         var panelId = panel.getId();
32063         this.panels.removeKey(panelId);
32064         if(preservePanel){
32065             document.body.appendChild(panel.getEl().dom);
32066         }
32067         if(this.tabs){
32068             this.tabs.removeTab(panel.getEl().id);
32069         }else if (!preservePanel){
32070             this.bodyEl.dom.removeChild(panel.getEl().dom);
32071         }
32072         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32073             var p = this.panels.first();
32074             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32075             tempEl.appendChild(p.getEl().dom);
32076             this.bodyEl.update("");
32077             this.bodyEl.dom.appendChild(p.getEl().dom);
32078             tempEl = null;
32079             this.updateTitle(p.getTitle());
32080             this.tabs = null;
32081             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32082             this.setActivePanel(p);
32083         }
32084         panel.setRegion(null);
32085         if(this.activePanel == panel){
32086             this.activePanel = null;
32087         }
32088         if(this.config.autoDestroy !== false && preservePanel !== true){
32089             try{panel.destroy();}catch(e){}
32090         }
32091         this.fireEvent("panelremoved", this, panel);
32092         return panel;
32093     },
32094
32095     /**
32096      * Returns the TabPanel component used by this region
32097      * @return {Roo.TabPanel}
32098      */
32099     getTabs : function(){
32100         return this.tabs;
32101     },
32102
32103     createTool : function(parentEl, className){
32104         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
32105             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
32106         btn.addClassOnOver("x-layout-tools-button-over");
32107         return btn;
32108     }
32109 });/*
32110  * Based on:
32111  * Ext JS Library 1.1.1
32112  * Copyright(c) 2006-2007, Ext JS, LLC.
32113  *
32114  * Originally Released Under LGPL - original licence link has changed is not relivant.
32115  *
32116  * Fork - LGPL
32117  * <script type="text/javascript">
32118  */
32119  
32120
32121
32122 /**
32123  * @class Roo.SplitLayoutRegion
32124  * @extends Roo.LayoutRegion
32125  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32126  */
32127 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
32128     this.cursor = cursor;
32129     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
32130 };
32131
32132 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
32133     splitTip : "Drag to resize.",
32134     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32135     useSplitTips : false,
32136
32137     applyConfig : function(config){
32138         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
32139         if(config.split){
32140             if(!this.split){
32141                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
32142                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
32143                 /** The SplitBar for this region 
32144                 * @type Roo.SplitBar */
32145                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
32146                 this.split.on("moved", this.onSplitMove, this);
32147                 this.split.useShim = config.useShim === true;
32148                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32149                 if(this.useSplitTips){
32150                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32151                 }
32152                 if(config.collapsible){
32153                     this.split.el.on("dblclick", this.collapse,  this);
32154                 }
32155             }
32156             if(typeof config.minSize != "undefined"){
32157                 this.split.minSize = config.minSize;
32158             }
32159             if(typeof config.maxSize != "undefined"){
32160                 this.split.maxSize = config.maxSize;
32161             }
32162             if(config.hideWhenEmpty || config.hidden || config.collapsed){
32163                 this.hideSplitter();
32164             }
32165         }
32166     },
32167
32168     getHMaxSize : function(){
32169          var cmax = this.config.maxSize || 10000;
32170          var center = this.mgr.getRegion("center");
32171          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32172     },
32173
32174     getVMaxSize : function(){
32175          var cmax = this.config.maxSize || 10000;
32176          var center = this.mgr.getRegion("center");
32177          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32178     },
32179
32180     onSplitMove : function(split, newSize){
32181         this.fireEvent("resized", this, newSize);
32182     },
32183     
32184     /** 
32185      * Returns the {@link Roo.SplitBar} for this region.
32186      * @return {Roo.SplitBar}
32187      */
32188     getSplitBar : function(){
32189         return this.split;
32190     },
32191     
32192     hide : function(){
32193         this.hideSplitter();
32194         Roo.SplitLayoutRegion.superclass.hide.call(this);
32195     },
32196
32197     hideSplitter : function(){
32198         if(this.split){
32199             this.split.el.setLocation(-2000,-2000);
32200             this.split.el.hide();
32201         }
32202     },
32203
32204     show : function(){
32205         if(this.split){
32206             this.split.el.show();
32207         }
32208         Roo.SplitLayoutRegion.superclass.show.call(this);
32209     },
32210     
32211     beforeSlide: function(){
32212         if(Roo.isGecko){// firefox overflow auto bug workaround
32213             this.bodyEl.clip();
32214             if(this.tabs) {
32215                 this.tabs.bodyEl.clip();
32216             }
32217             if(this.activePanel){
32218                 this.activePanel.getEl().clip();
32219                 
32220                 if(this.activePanel.beforeSlide){
32221                     this.activePanel.beforeSlide();
32222                 }
32223             }
32224         }
32225     },
32226     
32227     afterSlide : function(){
32228         if(Roo.isGecko){// firefox overflow auto bug workaround
32229             this.bodyEl.unclip();
32230             if(this.tabs) {
32231                 this.tabs.bodyEl.unclip();
32232             }
32233             if(this.activePanel){
32234                 this.activePanel.getEl().unclip();
32235                 if(this.activePanel.afterSlide){
32236                     this.activePanel.afterSlide();
32237                 }
32238             }
32239         }
32240     },
32241
32242     initAutoHide : function(){
32243         if(this.autoHide !== false){
32244             if(!this.autoHideHd){
32245                 var st = new Roo.util.DelayedTask(this.slideIn, this);
32246                 this.autoHideHd = {
32247                     "mouseout": function(e){
32248                         if(!e.within(this.el, true)){
32249                             st.delay(500);
32250                         }
32251                     },
32252                     "mouseover" : function(e){
32253                         st.cancel();
32254                     },
32255                     scope : this
32256                 };
32257             }
32258             this.el.on(this.autoHideHd);
32259         }
32260     },
32261
32262     clearAutoHide : function(){
32263         if(this.autoHide !== false){
32264             this.el.un("mouseout", this.autoHideHd.mouseout);
32265             this.el.un("mouseover", this.autoHideHd.mouseover);
32266         }
32267     },
32268
32269     clearMonitor : function(){
32270         Roo.get(document).un("click", this.slideInIf, this);
32271     },
32272
32273     // these names are backwards but not changed for compat
32274     slideOut : function(){
32275         if(this.isSlid || this.el.hasActiveFx()){
32276             return;
32277         }
32278         this.isSlid = true;
32279         if(this.collapseBtn){
32280             this.collapseBtn.hide();
32281         }
32282         this.closeBtnState = this.closeBtn.getStyle('display');
32283         this.closeBtn.hide();
32284         if(this.stickBtn){
32285             this.stickBtn.show();
32286         }
32287         this.el.show();
32288         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32289         this.beforeSlide();
32290         this.el.setStyle("z-index", 10001);
32291         this.el.slideIn(this.getSlideAnchor(), {
32292             callback: function(){
32293                 this.afterSlide();
32294                 this.initAutoHide();
32295                 Roo.get(document).on("click", this.slideInIf, this);
32296                 this.fireEvent("slideshow", this);
32297             },
32298             scope: this,
32299             block: true
32300         });
32301     },
32302
32303     afterSlideIn : function(){
32304         this.clearAutoHide();
32305         this.isSlid = false;
32306         this.clearMonitor();
32307         this.el.setStyle("z-index", "");
32308         if(this.collapseBtn){
32309             this.collapseBtn.show();
32310         }
32311         this.closeBtn.setStyle('display', this.closeBtnState);
32312         if(this.stickBtn){
32313             this.stickBtn.hide();
32314         }
32315         this.fireEvent("slidehide", this);
32316     },
32317
32318     slideIn : function(cb){
32319         if(!this.isSlid || this.el.hasActiveFx()){
32320             Roo.callback(cb);
32321             return;
32322         }
32323         this.isSlid = false;
32324         this.beforeSlide();
32325         this.el.slideOut(this.getSlideAnchor(), {
32326             callback: function(){
32327                 this.el.setLeftTop(-10000, -10000);
32328                 this.afterSlide();
32329                 this.afterSlideIn();
32330                 Roo.callback(cb);
32331             },
32332             scope: this,
32333             block: true
32334         });
32335     },
32336     
32337     slideInIf : function(e){
32338         if(!e.within(this.el)){
32339             this.slideIn();
32340         }
32341     },
32342
32343     animateCollapse : function(){
32344         this.beforeSlide();
32345         this.el.setStyle("z-index", 20000);
32346         var anchor = this.getSlideAnchor();
32347         this.el.slideOut(anchor, {
32348             callback : function(){
32349                 this.el.setStyle("z-index", "");
32350                 this.collapsedEl.slideIn(anchor, {duration:.3});
32351                 this.afterSlide();
32352                 this.el.setLocation(-10000,-10000);
32353                 this.el.hide();
32354                 this.fireEvent("collapsed", this);
32355             },
32356             scope: this,
32357             block: true
32358         });
32359     },
32360
32361     animateExpand : function(){
32362         this.beforeSlide();
32363         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
32364         this.el.setStyle("z-index", 20000);
32365         this.collapsedEl.hide({
32366             duration:.1
32367         });
32368         this.el.slideIn(this.getSlideAnchor(), {
32369             callback : function(){
32370                 this.el.setStyle("z-index", "");
32371                 this.afterSlide();
32372                 if(this.split){
32373                     this.split.el.show();
32374                 }
32375                 this.fireEvent("invalidated", this);
32376                 this.fireEvent("expanded", this);
32377             },
32378             scope: this,
32379             block: true
32380         });
32381     },
32382
32383     anchors : {
32384         "west" : "left",
32385         "east" : "right",
32386         "north" : "top",
32387         "south" : "bottom"
32388     },
32389
32390     sanchors : {
32391         "west" : "l",
32392         "east" : "r",
32393         "north" : "t",
32394         "south" : "b"
32395     },
32396
32397     canchors : {
32398         "west" : "tl-tr",
32399         "east" : "tr-tl",
32400         "north" : "tl-bl",
32401         "south" : "bl-tl"
32402     },
32403
32404     getAnchor : function(){
32405         return this.anchors[this.position];
32406     },
32407
32408     getCollapseAnchor : function(){
32409         return this.canchors[this.position];
32410     },
32411
32412     getSlideAnchor : function(){
32413         return this.sanchors[this.position];
32414     },
32415
32416     getAlignAdj : function(){
32417         var cm = this.cmargins;
32418         switch(this.position){
32419             case "west":
32420                 return [0, 0];
32421             break;
32422             case "east":
32423                 return [0, 0];
32424             break;
32425             case "north":
32426                 return [0, 0];
32427             break;
32428             case "south":
32429                 return [0, 0];
32430             break;
32431         }
32432     },
32433
32434     getExpandAdj : function(){
32435         var c = this.collapsedEl, cm = this.cmargins;
32436         switch(this.position){
32437             case "west":
32438                 return [-(cm.right+c.getWidth()+cm.left), 0];
32439             break;
32440             case "east":
32441                 return [cm.right+c.getWidth()+cm.left, 0];
32442             break;
32443             case "north":
32444                 return [0, -(cm.top+cm.bottom+c.getHeight())];
32445             break;
32446             case "south":
32447                 return [0, cm.top+cm.bottom+c.getHeight()];
32448             break;
32449         }
32450     }
32451 });/*
32452  * Based on:
32453  * Ext JS Library 1.1.1
32454  * Copyright(c) 2006-2007, Ext JS, LLC.
32455  *
32456  * Originally Released Under LGPL - original licence link has changed is not relivant.
32457  *
32458  * Fork - LGPL
32459  * <script type="text/javascript">
32460  */
32461 /*
32462  * These classes are private internal classes
32463  */
32464 Roo.CenterLayoutRegion = function(mgr, config){
32465     Roo.LayoutRegion.call(this, mgr, config, "center");
32466     this.visible = true;
32467     this.minWidth = config.minWidth || 20;
32468     this.minHeight = config.minHeight || 20;
32469 };
32470
32471 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
32472     hide : function(){
32473         // center panel can't be hidden
32474     },
32475     
32476     show : function(){
32477         // center panel can't be hidden
32478     },
32479     
32480     getMinWidth: function(){
32481         return this.minWidth;
32482     },
32483     
32484     getMinHeight: function(){
32485         return this.minHeight;
32486     }
32487 });
32488
32489
32490 Roo.NorthLayoutRegion = function(mgr, config){
32491     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
32492     if(this.split){
32493         this.split.placement = Roo.SplitBar.TOP;
32494         this.split.orientation = Roo.SplitBar.VERTICAL;
32495         this.split.el.addClass("x-layout-split-v");
32496     }
32497     var size = config.initialSize || config.height;
32498     if(typeof size != "undefined"){
32499         this.el.setHeight(size);
32500     }
32501 };
32502 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
32503     orientation: Roo.SplitBar.VERTICAL,
32504     getBox : function(){
32505         if(this.collapsed){
32506             return this.collapsedEl.getBox();
32507         }
32508         var box = this.el.getBox();
32509         if(this.split){
32510             box.height += this.split.el.getHeight();
32511         }
32512         return box;
32513     },
32514     
32515     updateBox : function(box){
32516         if(this.split && !this.collapsed){
32517             box.height -= this.split.el.getHeight();
32518             this.split.el.setLeft(box.x);
32519             this.split.el.setTop(box.y+box.height);
32520             this.split.el.setWidth(box.width);
32521         }
32522         if(this.collapsed){
32523             this.updateBody(box.width, null);
32524         }
32525         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32526     }
32527 });
32528
32529 Roo.SouthLayoutRegion = function(mgr, config){
32530     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
32531     if(this.split){
32532         this.split.placement = Roo.SplitBar.BOTTOM;
32533         this.split.orientation = Roo.SplitBar.VERTICAL;
32534         this.split.el.addClass("x-layout-split-v");
32535     }
32536     var size = config.initialSize || config.height;
32537     if(typeof size != "undefined"){
32538         this.el.setHeight(size);
32539     }
32540 };
32541 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
32542     orientation: Roo.SplitBar.VERTICAL,
32543     getBox : function(){
32544         if(this.collapsed){
32545             return this.collapsedEl.getBox();
32546         }
32547         var box = this.el.getBox();
32548         if(this.split){
32549             var sh = this.split.el.getHeight();
32550             box.height += sh;
32551             box.y -= sh;
32552         }
32553         return box;
32554     },
32555     
32556     updateBox : function(box){
32557         if(this.split && !this.collapsed){
32558             var sh = this.split.el.getHeight();
32559             box.height -= sh;
32560             box.y += sh;
32561             this.split.el.setLeft(box.x);
32562             this.split.el.setTop(box.y-sh);
32563             this.split.el.setWidth(box.width);
32564         }
32565         if(this.collapsed){
32566             this.updateBody(box.width, null);
32567         }
32568         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32569     }
32570 });
32571
32572 Roo.EastLayoutRegion = function(mgr, config){
32573     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
32574     if(this.split){
32575         this.split.placement = Roo.SplitBar.RIGHT;
32576         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32577         this.split.el.addClass("x-layout-split-h");
32578     }
32579     var size = config.initialSize || config.width;
32580     if(typeof size != "undefined"){
32581         this.el.setWidth(size);
32582     }
32583 };
32584 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
32585     orientation: Roo.SplitBar.HORIZONTAL,
32586     getBox : function(){
32587         if(this.collapsed){
32588             return this.collapsedEl.getBox();
32589         }
32590         var box = this.el.getBox();
32591         if(this.split){
32592             var sw = this.split.el.getWidth();
32593             box.width += sw;
32594             box.x -= sw;
32595         }
32596         return box;
32597     },
32598
32599     updateBox : function(box){
32600         if(this.split && !this.collapsed){
32601             var sw = this.split.el.getWidth();
32602             box.width -= sw;
32603             this.split.el.setLeft(box.x);
32604             this.split.el.setTop(box.y);
32605             this.split.el.setHeight(box.height);
32606             box.x += sw;
32607         }
32608         if(this.collapsed){
32609             this.updateBody(null, box.height);
32610         }
32611         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32612     }
32613 });
32614
32615 Roo.WestLayoutRegion = function(mgr, config){
32616     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
32617     if(this.split){
32618         this.split.placement = Roo.SplitBar.LEFT;
32619         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32620         this.split.el.addClass("x-layout-split-h");
32621     }
32622     var size = config.initialSize || config.width;
32623     if(typeof size != "undefined"){
32624         this.el.setWidth(size);
32625     }
32626 };
32627 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
32628     orientation: Roo.SplitBar.HORIZONTAL,
32629     getBox : function(){
32630         if(this.collapsed){
32631             return this.collapsedEl.getBox();
32632         }
32633         var box = this.el.getBox();
32634         if(this.split){
32635             box.width += this.split.el.getWidth();
32636         }
32637         return box;
32638     },
32639     
32640     updateBox : function(box){
32641         if(this.split && !this.collapsed){
32642             var sw = this.split.el.getWidth();
32643             box.width -= sw;
32644             this.split.el.setLeft(box.x+box.width);
32645             this.split.el.setTop(box.y);
32646             this.split.el.setHeight(box.height);
32647         }
32648         if(this.collapsed){
32649             this.updateBody(null, box.height);
32650         }
32651         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32652     }
32653 });
32654 /*
32655  * Based on:
32656  * Ext JS Library 1.1.1
32657  * Copyright(c) 2006-2007, Ext JS, LLC.
32658  *
32659  * Originally Released Under LGPL - original licence link has changed is not relivant.
32660  *
32661  * Fork - LGPL
32662  * <script type="text/javascript">
32663  */
32664  
32665  
32666 /*
32667  * Private internal class for reading and applying state
32668  */
32669 Roo.LayoutStateManager = function(layout){
32670      // default empty state
32671      this.state = {
32672         north: {},
32673         south: {},
32674         east: {},
32675         west: {}       
32676     };
32677 };
32678
32679 Roo.LayoutStateManager.prototype = {
32680     init : function(layout, provider){
32681         this.provider = provider;
32682         var state = provider.get(layout.id+"-layout-state");
32683         if(state){
32684             var wasUpdating = layout.isUpdating();
32685             if(!wasUpdating){
32686                 layout.beginUpdate();
32687             }
32688             for(var key in state){
32689                 if(typeof state[key] != "function"){
32690                     var rstate = state[key];
32691                     var r = layout.getRegion(key);
32692                     if(r && rstate){
32693                         if(rstate.size){
32694                             r.resizeTo(rstate.size);
32695                         }
32696                         if(rstate.collapsed == true){
32697                             r.collapse(true);
32698                         }else{
32699                             r.expand(null, true);
32700                         }
32701                     }
32702                 }
32703             }
32704             if(!wasUpdating){
32705                 layout.endUpdate();
32706             }
32707             this.state = state; 
32708         }
32709         this.layout = layout;
32710         layout.on("regionresized", this.onRegionResized, this);
32711         layout.on("regioncollapsed", this.onRegionCollapsed, this);
32712         layout.on("regionexpanded", this.onRegionExpanded, this);
32713     },
32714     
32715     storeState : function(){
32716         this.provider.set(this.layout.id+"-layout-state", this.state);
32717     },
32718     
32719     onRegionResized : function(region, newSize){
32720         this.state[region.getPosition()].size = newSize;
32721         this.storeState();
32722     },
32723     
32724     onRegionCollapsed : function(region){
32725         this.state[region.getPosition()].collapsed = true;
32726         this.storeState();
32727     },
32728     
32729     onRegionExpanded : function(region){
32730         this.state[region.getPosition()].collapsed = false;
32731         this.storeState();
32732     }
32733 };/*
32734  * Based on:
32735  * Ext JS Library 1.1.1
32736  * Copyright(c) 2006-2007, Ext JS, LLC.
32737  *
32738  * Originally Released Under LGPL - original licence link has changed is not relivant.
32739  *
32740  * Fork - LGPL
32741  * <script type="text/javascript">
32742  */
32743 /**
32744  * @class Roo.ContentPanel
32745  * @extends Roo.util.Observable
32746  * @children Roo.form.Form Roo.JsonView Roo.View
32747  * @parent Roo.BorderLayout Roo.LayoutDialog builder-top
32748  * A basic ContentPanel element.
32749  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
32750  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
32751  * @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
32752  * @cfg {Boolean}   closable      True if the panel can be closed/removed
32753  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
32754  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
32755  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
32756  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
32757  * @cfg {String} title          The title for this panel
32758  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
32759  * @cfg {String} url            Calls {@link #setUrl} with this value
32760  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
32761  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
32762  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
32763  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
32764  * @cfg {String}    style  Extra style to add to the content panel
32765  * @cfg {Roo.menu.Menu} menu  popup menu
32766
32767  * @constructor
32768  * Create a new ContentPanel.
32769  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
32770  * @param {String/Object} config A string to set only the title or a config object
32771  * @param {String} content (optional) Set the HTML content for this panel
32772  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
32773  */
32774 Roo.ContentPanel = function(el, config, content){
32775     
32776      
32777     /*
32778     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
32779         config = el;
32780         el = Roo.id();
32781     }
32782     if (config && config.parentLayout) { 
32783         el = config.parentLayout.el.createChild(); 
32784     }
32785     */
32786     if(el.autoCreate){ // xtype is available if this is called from factory
32787         config = el;
32788         el = Roo.id();
32789     }
32790     this.el = Roo.get(el);
32791     if(!this.el && config && config.autoCreate){
32792         if(typeof config.autoCreate == "object"){
32793             if(!config.autoCreate.id){
32794                 config.autoCreate.id = config.id||el;
32795             }
32796             this.el = Roo.DomHelper.append(document.body,
32797                         config.autoCreate, true);
32798         }else{
32799             this.el = Roo.DomHelper.append(document.body,
32800                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
32801         }
32802     }
32803     
32804     
32805     this.closable = false;
32806     this.loaded = false;
32807     this.active = false;
32808     if(typeof config == "string"){
32809         this.title = config;
32810     }else{
32811         Roo.apply(this, config);
32812     }
32813     
32814     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
32815         this.wrapEl = this.el.wrap();
32816         this.toolbar.container = this.el.insertSibling(false, 'before');
32817         this.toolbar = new Roo.Toolbar(this.toolbar);
32818     }
32819     
32820     // xtype created footer. - not sure if will work as we normally have to render first..
32821     if (this.footer && !this.footer.el && this.footer.xtype) {
32822         if (!this.wrapEl) {
32823             this.wrapEl = this.el.wrap();
32824         }
32825     
32826         this.footer.container = this.wrapEl.createChild();
32827          
32828         this.footer = Roo.factory(this.footer, Roo);
32829         
32830     }
32831     
32832     if(this.resizeEl){
32833         this.resizeEl = Roo.get(this.resizeEl, true);
32834     }else{
32835         this.resizeEl = this.el;
32836     }
32837     // handle view.xtype
32838     
32839  
32840     
32841     
32842     this.addEvents({
32843         /**
32844          * @event activate
32845          * Fires when this panel is activated. 
32846          * @param {Roo.ContentPanel} this
32847          */
32848         "activate" : true,
32849         /**
32850          * @event deactivate
32851          * Fires when this panel is activated. 
32852          * @param {Roo.ContentPanel} this
32853          */
32854         "deactivate" : true,
32855
32856         /**
32857          * @event resize
32858          * Fires when this panel is resized if fitToFrame is true.
32859          * @param {Roo.ContentPanel} this
32860          * @param {Number} width The width after any component adjustments
32861          * @param {Number} height The height after any component adjustments
32862          */
32863         "resize" : true,
32864         
32865          /**
32866          * @event render
32867          * Fires when this tab is created
32868          * @param {Roo.ContentPanel} this
32869          */
32870         "render" : true
32871          
32872         
32873     });
32874     
32875
32876     
32877     
32878     if(this.autoScroll){
32879         this.resizeEl.setStyle("overflow", "auto");
32880     } else {
32881         // fix randome scrolling
32882         this.el.on('scroll', function() {
32883             Roo.log('fix random scolling');
32884             this.scrollTo('top',0); 
32885         });
32886     }
32887     content = content || this.content;
32888     if(content){
32889         this.setContent(content);
32890     }
32891     if(config && config.url){
32892         this.setUrl(this.url, this.params, this.loadOnce);
32893     }
32894     
32895     
32896     
32897     Roo.ContentPanel.superclass.constructor.call(this);
32898     
32899     if (this.view && typeof(this.view.xtype) != 'undefined') {
32900         this.view.el = this.el.appendChild(document.createElement("div"));
32901         this.view = Roo.factory(this.view); 
32902         this.view.render  &&  this.view.render(false, '');  
32903     }
32904     
32905     
32906     this.fireEvent('render', this);
32907 };
32908
32909 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
32910     tabTip:'',
32911     setRegion : function(region){
32912         this.region = region;
32913         if(region){
32914            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
32915         }else{
32916            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
32917         } 
32918     },
32919     
32920     /**
32921      * Returns the toolbar for this Panel if one was configured. 
32922      * @return {Roo.Toolbar} 
32923      */
32924     getToolbar : function(){
32925         return this.toolbar;
32926     },
32927     
32928     setActiveState : function(active){
32929         this.active = active;
32930         if(!active){
32931             this.fireEvent("deactivate", this);
32932         }else{
32933             this.fireEvent("activate", this);
32934         }
32935     },
32936     /**
32937      * Updates this panel's element
32938      * @param {String} content The new content
32939      * @param {Boolean} loadScripts (optional) true to look for and process scripts
32940     */
32941     setContent : function(content, loadScripts){
32942         this.el.update(content, loadScripts);
32943     },
32944
32945     ignoreResize : function(w, h){
32946         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
32947             return true;
32948         }else{
32949             this.lastSize = {width: w, height: h};
32950             return false;
32951         }
32952     },
32953     /**
32954      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
32955      * @return {Roo.UpdateManager} The UpdateManager
32956      */
32957     getUpdateManager : function(){
32958         return this.el.getUpdateManager();
32959     },
32960      /**
32961      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
32962      * @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:
32963 <pre><code>
32964 panel.load({
32965     url: "your-url.php",
32966     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
32967     callback: yourFunction,
32968     scope: yourObject, //(optional scope)
32969     discardUrl: false,
32970     nocache: false,
32971     text: "Loading...",
32972     timeout: 30,
32973     scripts: false
32974 });
32975 </code></pre>
32976      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
32977      * 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.
32978      * @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}
32979      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
32980      * @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.
32981      * @return {Roo.ContentPanel} this
32982      */
32983     load : function(){
32984         var um = this.el.getUpdateManager();
32985         um.update.apply(um, arguments);
32986         return this;
32987     },
32988
32989
32990     /**
32991      * 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.
32992      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
32993      * @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)
32994      * @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)
32995      * @return {Roo.UpdateManager} The UpdateManager
32996      */
32997     setUrl : function(url, params, loadOnce){
32998         if(this.refreshDelegate){
32999             this.removeListener("activate", this.refreshDelegate);
33000         }
33001         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33002         this.on("activate", this.refreshDelegate);
33003         return this.el.getUpdateManager();
33004     },
33005     
33006     _handleRefresh : function(url, params, loadOnce){
33007         if(!loadOnce || !this.loaded){
33008             var updater = this.el.getUpdateManager();
33009             updater.update(url, params, this._setLoaded.createDelegate(this));
33010         }
33011     },
33012     
33013     _setLoaded : function(){
33014         this.loaded = true;
33015     }, 
33016     
33017     /**
33018      * Returns this panel's id
33019      * @return {String} 
33020      */
33021     getId : function(){
33022         return this.el.id;
33023     },
33024     
33025     /** 
33026      * Returns this panel's element - used by regiosn to add.
33027      * @return {Roo.Element} 
33028      */
33029     getEl : function(){
33030         return this.wrapEl || this.el;
33031     },
33032     
33033     adjustForComponents : function(width, height)
33034     {
33035         //Roo.log('adjustForComponents ');
33036         if(this.resizeEl != this.el){
33037             width -= this.el.getFrameWidth('lr');
33038             height -= this.el.getFrameWidth('tb');
33039         }
33040         if(this.toolbar){
33041             var te = this.toolbar.getEl();
33042             height -= te.getHeight();
33043             te.setWidth(width);
33044         }
33045         if(this.footer){
33046             var te = this.footer.getEl();
33047             //Roo.log("footer:" + te.getHeight());
33048             
33049             height -= te.getHeight();
33050             te.setWidth(width);
33051         }
33052         
33053         
33054         if(this.adjustments){
33055             width += this.adjustments[0];
33056             height += this.adjustments[1];
33057         }
33058         return {"width": width, "height": height};
33059     },
33060     
33061     setSize : function(width, height){
33062         if(this.fitToFrame && !this.ignoreResize(width, height)){
33063             if(this.fitContainer && this.resizeEl != this.el){
33064                 this.el.setSize(width, height);
33065             }
33066             var size = this.adjustForComponents(width, height);
33067             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33068             this.fireEvent('resize', this, size.width, size.height);
33069         }
33070     },
33071     
33072     /**
33073      * Returns this panel's title
33074      * @return {String} 
33075      */
33076     getTitle : function(){
33077         return this.title;
33078     },
33079     
33080     /**
33081      * Set this panel's title
33082      * @param {String} title
33083      */
33084     setTitle : function(title){
33085         this.title = title;
33086         if(this.region){
33087             this.region.updatePanelTitle(this, title);
33088         }
33089     },
33090     
33091     /**
33092      * Returns true is this panel was configured to be closable
33093      * @return {Boolean} 
33094      */
33095     isClosable : function(){
33096         return this.closable;
33097     },
33098     
33099     beforeSlide : function(){
33100         this.el.clip();
33101         this.resizeEl.clip();
33102     },
33103     
33104     afterSlide : function(){
33105         this.el.unclip();
33106         this.resizeEl.unclip();
33107     },
33108     
33109     /**
33110      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33111      *   Will fail silently if the {@link #setUrl} method has not been called.
33112      *   This does not activate the panel, just updates its content.
33113      */
33114     refresh : function(){
33115         if(this.refreshDelegate){
33116            this.loaded = false;
33117            this.refreshDelegate();
33118         }
33119     },
33120     
33121     /**
33122      * Destroys this panel
33123      */
33124     destroy : function(){
33125         this.el.removeAllListeners();
33126         var tempEl = document.createElement("span");
33127         tempEl.appendChild(this.el.dom);
33128         tempEl.innerHTML = "";
33129         this.el.remove();
33130         this.el = null;
33131     },
33132     
33133     /**
33134      * form - if the content panel contains a form - this is a reference to it.
33135      * @type {Roo.form.Form}
33136      */
33137     form : false,
33138     /**
33139      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33140      *    This contains a reference to it.
33141      * @type {Roo.View}
33142      */
33143     view : false,
33144     
33145       /**
33146      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33147      * <pre><code>
33148
33149 layout.addxtype({
33150        xtype : 'Form',
33151        items: [ .... ]
33152    }
33153 );
33154
33155 </code></pre>
33156      * @param {Object} cfg Xtype definition of item to add.
33157      */
33158     
33159     addxtype : function(cfg) {
33160         // add form..
33161         if (cfg.xtype.match(/^Form$/)) {
33162             
33163             var el;
33164             //if (this.footer) {
33165             //    el = this.footer.container.insertSibling(false, 'before');
33166             //} else {
33167                 el = this.el.createChild();
33168             //}
33169
33170             this.form = new  Roo.form.Form(cfg);
33171             
33172             
33173             if ( this.form.allItems.length) {
33174                 this.form.render(el.dom);
33175             }
33176             return this.form;
33177         }
33178         // should only have one of theses..
33179         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33180             // views.. should not be just added - used named prop 'view''
33181             
33182             cfg.el = this.el.appendChild(document.createElement("div"));
33183             // factory?
33184             
33185             var ret = new Roo.factory(cfg);
33186              
33187              ret.render && ret.render(false, ''); // render blank..
33188             this.view = ret;
33189             return ret;
33190         }
33191         return false;
33192     }
33193 });
33194
33195 /**
33196  * @class Roo.GridPanel
33197  * @extends Roo.ContentPanel
33198  * @constructor
33199  * Create a new GridPanel.
33200  * @param {Roo.grid.Grid} grid The grid for this panel
33201  * @param {String/Object} config A string to set only the panel's title, or a config object
33202  */
33203 Roo.GridPanel = function(grid, config){
33204     
33205   
33206     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33207         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
33208         
33209     this.wrapper.dom.appendChild(grid.getGridEl().dom);
33210     
33211     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
33212     
33213     if(this.toolbar){
33214         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
33215     }
33216     // xtype created footer. - not sure if will work as we normally have to render first..
33217     if (this.footer && !this.footer.el && this.footer.xtype) {
33218         
33219         this.footer.container = this.grid.getView().getFooterPanel(true);
33220         this.footer.dataSource = this.grid.dataSource;
33221         this.footer = Roo.factory(this.footer, Roo);
33222         
33223     }
33224     
33225     grid.monitorWindowResize = false; // turn off autosizing
33226     grid.autoHeight = false;
33227     grid.autoWidth = false;
33228     this.grid = grid;
33229     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
33230 };
33231
33232 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
33233     getId : function(){
33234         return this.grid.id;
33235     },
33236     
33237     /**
33238      * Returns the grid for this panel
33239      * @return {Roo.grid.Grid} 
33240      */
33241     getGrid : function(){
33242         return this.grid;    
33243     },
33244     
33245     setSize : function(width, height){
33246         if(!this.ignoreResize(width, height)){
33247             var grid = this.grid;
33248             var size = this.adjustForComponents(width, height);
33249             grid.getGridEl().setSize(size.width, size.height);
33250             grid.autoSize();
33251         }
33252     },
33253     
33254     beforeSlide : function(){
33255         this.grid.getView().scroller.clip();
33256     },
33257     
33258     afterSlide : function(){
33259         this.grid.getView().scroller.unclip();
33260     },
33261     
33262     destroy : function(){
33263         this.grid.destroy();
33264         delete this.grid;
33265         Roo.GridPanel.superclass.destroy.call(this); 
33266     }
33267 });
33268
33269
33270 /**
33271  * @class Roo.NestedLayoutPanel
33272  * @extends Roo.ContentPanel
33273  * @constructor
33274  * Create a new NestedLayoutPanel.
33275  * 
33276  * 
33277  * @param {Roo.BorderLayout} layout [required] The layout for this panel
33278  * @param {String/Object} config A string to set only the title or a config object
33279  */
33280 Roo.NestedLayoutPanel = function(layout, config)
33281 {
33282     // construct with only one argument..
33283     /* FIXME - implement nicer consturctors
33284     if (layout.layout) {
33285         config = layout;
33286         layout = config.layout;
33287         delete config.layout;
33288     }
33289     if (layout.xtype && !layout.getEl) {
33290         // then layout needs constructing..
33291         layout = Roo.factory(layout, Roo);
33292     }
33293     */
33294     
33295     
33296     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
33297     
33298     layout.monitorWindowResize = false; // turn off autosizing
33299     this.layout = layout;
33300     this.layout.getEl().addClass("x-layout-nested-layout");
33301     
33302     
33303     
33304     
33305 };
33306
33307 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
33308
33309     setSize : function(width, height){
33310         if(!this.ignoreResize(width, height)){
33311             var size = this.adjustForComponents(width, height);
33312             var el = this.layout.getEl();
33313             el.setSize(size.width, size.height);
33314             var touch = el.dom.offsetWidth;
33315             this.layout.layout();
33316             // ie requires a double layout on the first pass
33317             if(Roo.isIE && !this.initialized){
33318                 this.initialized = true;
33319                 this.layout.layout();
33320             }
33321         }
33322     },
33323     
33324     // activate all subpanels if not currently active..
33325     
33326     setActiveState : function(active){
33327         this.active = active;
33328         if(!active){
33329             this.fireEvent("deactivate", this);
33330             return;
33331         }
33332         
33333         this.fireEvent("activate", this);
33334         // not sure if this should happen before or after..
33335         if (!this.layout) {
33336             return; // should not happen..
33337         }
33338         var reg = false;
33339         for (var r in this.layout.regions) {
33340             reg = this.layout.getRegion(r);
33341             if (reg.getActivePanel()) {
33342                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
33343                 reg.setActivePanel(reg.getActivePanel());
33344                 continue;
33345             }
33346             if (!reg.panels.length) {
33347                 continue;
33348             }
33349             reg.showPanel(reg.getPanel(0));
33350         }
33351         
33352         
33353         
33354         
33355     },
33356     
33357     /**
33358      * Returns the nested BorderLayout for this panel
33359      * @return {Roo.BorderLayout} 
33360      */
33361     getLayout : function(){
33362         return this.layout;
33363     },
33364     
33365      /**
33366      * Adds a xtype elements to the layout of the nested panel
33367      * <pre><code>
33368
33369 panel.addxtype({
33370        xtype : 'ContentPanel',
33371        region: 'west',
33372        items: [ .... ]
33373    }
33374 );
33375
33376 panel.addxtype({
33377         xtype : 'NestedLayoutPanel',
33378         region: 'west',
33379         layout: {
33380            center: { },
33381            west: { }   
33382         },
33383         items : [ ... list of content panels or nested layout panels.. ]
33384    }
33385 );
33386 </code></pre>
33387      * @param {Object} cfg Xtype definition of item to add.
33388      */
33389     addxtype : function(cfg) {
33390         return this.layout.addxtype(cfg);
33391     
33392     }
33393 });
33394
33395 Roo.ScrollPanel = function(el, config, content){
33396     config = config || {};
33397     config.fitToFrame = true;
33398     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
33399     
33400     this.el.dom.style.overflow = "hidden";
33401     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
33402     this.el.removeClass("x-layout-inactive-content");
33403     this.el.on("mousewheel", this.onWheel, this);
33404
33405     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
33406     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
33407     up.unselectable(); down.unselectable();
33408     up.on("click", this.scrollUp, this);
33409     down.on("click", this.scrollDown, this);
33410     up.addClassOnOver("x-scroller-btn-over");
33411     down.addClassOnOver("x-scroller-btn-over");
33412     up.addClassOnClick("x-scroller-btn-click");
33413     down.addClassOnClick("x-scroller-btn-click");
33414     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
33415
33416     this.resizeEl = this.el;
33417     this.el = wrap; this.up = up; this.down = down;
33418 };
33419
33420 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
33421     increment : 100,
33422     wheelIncrement : 5,
33423     scrollUp : function(){
33424         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
33425     },
33426
33427     scrollDown : function(){
33428         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
33429     },
33430
33431     afterScroll : function(){
33432         var el = this.resizeEl;
33433         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
33434         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33435         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33436     },
33437
33438     setSize : function(){
33439         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
33440         this.afterScroll();
33441     },
33442
33443     onWheel : function(e){
33444         var d = e.getWheelDelta();
33445         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
33446         this.afterScroll();
33447         e.stopEvent();
33448     },
33449
33450     setContent : function(content, loadScripts){
33451         this.resizeEl.update(content, loadScripts);
33452     }
33453
33454 });
33455
33456
33457
33458 /**
33459  * @class Roo.TreePanel
33460  * @extends Roo.ContentPanel
33461  * Treepanel component
33462  * 
33463  * @constructor
33464  * Create a new TreePanel. - defaults to fit/scoll contents.
33465  * @param {String/Object} config A string to set only the panel's title, or a config object
33466  */
33467 Roo.TreePanel = function(config){
33468     var el = config.el;
33469     var tree = config.tree;
33470     delete config.tree; 
33471     delete config.el; // hopefull!
33472     
33473     // wrapper for IE7 strict & safari scroll issue
33474     
33475     var treeEl = el.createChild();
33476     config.resizeEl = treeEl;
33477     
33478     
33479     
33480     Roo.TreePanel.superclass.constructor.call(this, el, config);
33481  
33482  
33483     this.tree = new Roo.tree.TreePanel(treeEl , tree);
33484     //console.log(tree);
33485     this.on('activate', function()
33486     {
33487         if (this.tree.rendered) {
33488             return;
33489         }
33490         //console.log('render tree');
33491         this.tree.render();
33492     });
33493     // this should not be needed.. - it's actually the 'el' that resizes?
33494     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
33495     
33496     //this.on('resize',  function (cp, w, h) {
33497     //        this.tree.innerCt.setWidth(w);
33498     //        this.tree.innerCt.setHeight(h);
33499     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
33500     //});
33501
33502         
33503     
33504 };
33505
33506 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
33507     fitToFrame : true,
33508     autoScroll : true,
33509     /*
33510      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
33511      */
33512     tree : false
33513
33514 });
33515
33516
33517
33518
33519
33520
33521
33522
33523
33524
33525
33526 /*
33527  * Based on:
33528  * Ext JS Library 1.1.1
33529  * Copyright(c) 2006-2007, Ext JS, LLC.
33530  *
33531  * Originally Released Under LGPL - original licence link has changed is not relivant.
33532  *
33533  * Fork - LGPL
33534  * <script type="text/javascript">
33535  */
33536  
33537
33538 /**
33539  * @class Roo.ReaderLayout
33540  * @extends Roo.BorderLayout
33541  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
33542  * center region containing two nested regions (a top one for a list view and one for item preview below),
33543  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
33544  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
33545  * expedites the setup of the overall layout and regions for this common application style.
33546  * Example:
33547  <pre><code>
33548 var reader = new Roo.ReaderLayout();
33549 var CP = Roo.ContentPanel;  // shortcut for adding
33550
33551 reader.beginUpdate();
33552 reader.add("north", new CP("north", "North"));
33553 reader.add("west", new CP("west", {title: "West"}));
33554 reader.add("east", new CP("east", {title: "East"}));
33555
33556 reader.regions.listView.add(new CP("listView", "List"));
33557 reader.regions.preview.add(new CP("preview", "Preview"));
33558 reader.endUpdate();
33559 </code></pre>
33560 * @constructor
33561 * Create a new ReaderLayout
33562 * @param {Object} config Configuration options
33563 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
33564 * document.body if omitted)
33565 */
33566 Roo.ReaderLayout = function(config, renderTo){
33567     var c = config || {size:{}};
33568     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
33569         north: c.north !== false ? Roo.apply({
33570             split:false,
33571             initialSize: 32,
33572             titlebar: false
33573         }, c.north) : false,
33574         west: c.west !== false ? Roo.apply({
33575             split:true,
33576             initialSize: 200,
33577             minSize: 175,
33578             maxSize: 400,
33579             titlebar: true,
33580             collapsible: true,
33581             animate: true,
33582             margins:{left:5,right:0,bottom:5,top:5},
33583             cmargins:{left:5,right:5,bottom:5,top:5}
33584         }, c.west) : false,
33585         east: c.east !== false ? Roo.apply({
33586             split:true,
33587             initialSize: 200,
33588             minSize: 175,
33589             maxSize: 400,
33590             titlebar: true,
33591             collapsible: true,
33592             animate: true,
33593             margins:{left:0,right:5,bottom:5,top:5},
33594             cmargins:{left:5,right:5,bottom:5,top:5}
33595         }, c.east) : false,
33596         center: Roo.apply({
33597             tabPosition: 'top',
33598             autoScroll:false,
33599             closeOnTab: true,
33600             titlebar:false,
33601             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
33602         }, c.center)
33603     });
33604
33605     this.el.addClass('x-reader');
33606
33607     this.beginUpdate();
33608
33609     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
33610         south: c.preview !== false ? Roo.apply({
33611             split:true,
33612             initialSize: 200,
33613             minSize: 100,
33614             autoScroll:true,
33615             collapsible:true,
33616             titlebar: true,
33617             cmargins:{top:5,left:0, right:0, bottom:0}
33618         }, c.preview) : false,
33619         center: Roo.apply({
33620             autoScroll:false,
33621             titlebar:false,
33622             minHeight:200
33623         }, c.listView)
33624     });
33625     this.add('center', new Roo.NestedLayoutPanel(inner,
33626             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
33627
33628     this.endUpdate();
33629
33630     this.regions.preview = inner.getRegion('south');
33631     this.regions.listView = inner.getRegion('center');
33632 };
33633
33634 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
33635  * Based on:
33636  * Ext JS Library 1.1.1
33637  * Copyright(c) 2006-2007, Ext JS, LLC.
33638  *
33639  * Originally Released Under LGPL - original licence link has changed is not relivant.
33640  *
33641  * Fork - LGPL
33642  * <script type="text/javascript">
33643  */
33644  
33645 /**
33646  * @class Roo.grid.Grid
33647  * @extends Roo.util.Observable
33648  * This class represents the primary interface of a component based grid control.
33649  * <br><br>Usage:<pre><code>
33650  var grid = new Roo.grid.Grid("my-container-id", {
33651      ds: myDataStore,
33652      cm: myColModel,
33653      selModel: mySelectionModel,
33654      autoSizeColumns: true,
33655      monitorWindowResize: false,
33656      trackMouseOver: true
33657  });
33658  // set any options
33659  grid.render();
33660  * </code></pre>
33661  * <b>Common Problems:</b><br/>
33662  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
33663  * element will correct this<br/>
33664  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
33665  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
33666  * are unpredictable.<br/>
33667  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
33668  * grid to calculate dimensions/offsets.<br/>
33669   * @constructor
33670  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
33671  * The container MUST have some type of size defined for the grid to fill. The container will be
33672  * automatically set to position relative if it isn't already.
33673  * @param {Object} config A config object that sets properties on this grid.
33674  */
33675 Roo.grid.Grid = function(container, config){
33676         // initialize the container
33677         this.container = Roo.get(container);
33678         this.container.update("");
33679         this.container.setStyle("overflow", "hidden");
33680     this.container.addClass('x-grid-container');
33681
33682     this.id = this.container.id;
33683
33684     Roo.apply(this, config);
33685     // check and correct shorthanded configs
33686     if(this.ds){
33687         this.dataSource = this.ds;
33688         delete this.ds;
33689     }
33690     if(this.cm){
33691         this.colModel = this.cm;
33692         delete this.cm;
33693     }
33694     if(this.sm){
33695         this.selModel = this.sm;
33696         delete this.sm;
33697     }
33698
33699     if (this.selModel) {
33700         this.selModel = Roo.factory(this.selModel, Roo.grid);
33701         this.sm = this.selModel;
33702         this.sm.xmodule = this.xmodule || false;
33703     }
33704     if (typeof(this.colModel.config) == 'undefined') {
33705         this.colModel = new Roo.grid.ColumnModel(this.colModel);
33706         this.cm = this.colModel;
33707         this.cm.xmodule = this.xmodule || false;
33708     }
33709     if (this.dataSource) {
33710         this.dataSource= Roo.factory(this.dataSource, Roo.data);
33711         this.ds = this.dataSource;
33712         this.ds.xmodule = this.xmodule || false;
33713          
33714     }
33715     
33716     
33717     
33718     if(this.width){
33719         this.container.setWidth(this.width);
33720     }
33721
33722     if(this.height){
33723         this.container.setHeight(this.height);
33724     }
33725     /** @private */
33726         this.addEvents({
33727         // raw events
33728         /**
33729          * @event click
33730          * The raw click event for the entire grid.
33731          * @param {Roo.EventObject} e
33732          */
33733         "click" : true,
33734         /**
33735          * @event dblclick
33736          * The raw dblclick event for the entire grid.
33737          * @param {Roo.EventObject} e
33738          */
33739         "dblclick" : true,
33740         /**
33741          * @event contextmenu
33742          * The raw contextmenu event for the entire grid.
33743          * @param {Roo.EventObject} e
33744          */
33745         "contextmenu" : true,
33746         /**
33747          * @event mousedown
33748          * The raw mousedown event for the entire grid.
33749          * @param {Roo.EventObject} e
33750          */
33751         "mousedown" : true,
33752         /**
33753          * @event mouseup
33754          * The raw mouseup event for the entire grid.
33755          * @param {Roo.EventObject} e
33756          */
33757         "mouseup" : true,
33758         /**
33759          * @event mouseover
33760          * The raw mouseover event for the entire grid.
33761          * @param {Roo.EventObject} e
33762          */
33763         "mouseover" : true,
33764         /**
33765          * @event mouseout
33766          * The raw mouseout event for the entire grid.
33767          * @param {Roo.EventObject} e
33768          */
33769         "mouseout" : true,
33770         /**
33771          * @event keypress
33772          * The raw keypress event for the entire grid.
33773          * @param {Roo.EventObject} e
33774          */
33775         "keypress" : true,
33776         /**
33777          * @event keydown
33778          * The raw keydown event for the entire grid.
33779          * @param {Roo.EventObject} e
33780          */
33781         "keydown" : true,
33782
33783         // custom events
33784
33785         /**
33786          * @event cellclick
33787          * Fires when a cell is clicked
33788          * @param {Grid} this
33789          * @param {Number} rowIndex
33790          * @param {Number} columnIndex
33791          * @param {Roo.EventObject} e
33792          */
33793         "cellclick" : true,
33794         /**
33795          * @event celldblclick
33796          * Fires when a cell is double clicked
33797          * @param {Grid} this
33798          * @param {Number} rowIndex
33799          * @param {Number} columnIndex
33800          * @param {Roo.EventObject} e
33801          */
33802         "celldblclick" : true,
33803         /**
33804          * @event rowclick
33805          * Fires when a row is clicked
33806          * @param {Grid} this
33807          * @param {Number} rowIndex
33808          * @param {Roo.EventObject} e
33809          */
33810         "rowclick" : true,
33811         /**
33812          * @event rowdblclick
33813          * Fires when a row is double clicked
33814          * @param {Grid} this
33815          * @param {Number} rowIndex
33816          * @param {Roo.EventObject} e
33817          */
33818         "rowdblclick" : true,
33819         /**
33820          * @event headerclick
33821          * Fires when a header is clicked
33822          * @param {Grid} this
33823          * @param {Number} columnIndex
33824          * @param {Roo.EventObject} e
33825          */
33826         "headerclick" : true,
33827         /**
33828          * @event headerdblclick
33829          * Fires when a header cell is double clicked
33830          * @param {Grid} this
33831          * @param {Number} columnIndex
33832          * @param {Roo.EventObject} e
33833          */
33834         "headerdblclick" : true,
33835         /**
33836          * @event rowcontextmenu
33837          * Fires when a row is right clicked
33838          * @param {Grid} this
33839          * @param {Number} rowIndex
33840          * @param {Roo.EventObject} e
33841          */
33842         "rowcontextmenu" : true,
33843         /**
33844          * @event cellcontextmenu
33845          * Fires when a cell is right clicked
33846          * @param {Grid} this
33847          * @param {Number} rowIndex
33848          * @param {Number} cellIndex
33849          * @param {Roo.EventObject} e
33850          */
33851          "cellcontextmenu" : true,
33852         /**
33853          * @event headercontextmenu
33854          * Fires when a header is right clicked
33855          * @param {Grid} this
33856          * @param {Number} columnIndex
33857          * @param {Roo.EventObject} e
33858          */
33859         "headercontextmenu" : true,
33860         /**
33861          * @event bodyscroll
33862          * Fires when the body element is scrolled
33863          * @param {Number} scrollLeft
33864          * @param {Number} scrollTop
33865          */
33866         "bodyscroll" : true,
33867         /**
33868          * @event columnresize
33869          * Fires when the user resizes a column
33870          * @param {Number} columnIndex
33871          * @param {Number} newSize
33872          */
33873         "columnresize" : true,
33874         /**
33875          * @event columnmove
33876          * Fires when the user moves a column
33877          * @param {Number} oldIndex
33878          * @param {Number} newIndex
33879          */
33880         "columnmove" : true,
33881         /**
33882          * @event startdrag
33883          * Fires when row(s) start being dragged
33884          * @param {Grid} this
33885          * @param {Roo.GridDD} dd The drag drop object
33886          * @param {event} e The raw browser event
33887          */
33888         "startdrag" : true,
33889         /**
33890          * @event enddrag
33891          * Fires when a drag operation is complete
33892          * @param {Grid} this
33893          * @param {Roo.GridDD} dd The drag drop object
33894          * @param {event} e The raw browser event
33895          */
33896         "enddrag" : true,
33897         /**
33898          * @event dragdrop
33899          * Fires when dragged row(s) are dropped on a valid DD target
33900          * @param {Grid} this
33901          * @param {Roo.GridDD} dd The drag drop object
33902          * @param {String} targetId The target drag drop object
33903          * @param {event} e The raw browser event
33904          */
33905         "dragdrop" : true,
33906         /**
33907          * @event dragover
33908          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
33909          * @param {Grid} this
33910          * @param {Roo.GridDD} dd The drag drop object
33911          * @param {String} targetId The target drag drop object
33912          * @param {event} e The raw browser event
33913          */
33914         "dragover" : true,
33915         /**
33916          * @event dragenter
33917          *  Fires when the dragged row(s) first cross another DD target while being dragged
33918          * @param {Grid} this
33919          * @param {Roo.GridDD} dd The drag drop object
33920          * @param {String} targetId The target drag drop object
33921          * @param {event} e The raw browser event
33922          */
33923         "dragenter" : true,
33924         /**
33925          * @event dragout
33926          * Fires when the dragged row(s) leave another DD target while being dragged
33927          * @param {Grid} this
33928          * @param {Roo.GridDD} dd The drag drop object
33929          * @param {String} targetId The target drag drop object
33930          * @param {event} e The raw browser event
33931          */
33932         "dragout" : true,
33933         /**
33934          * @event rowclass
33935          * Fires when a row is rendered, so you can change add a style to it.
33936          * @param {GridView} gridview   The grid view
33937          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
33938          */
33939         'rowclass' : true,
33940
33941         /**
33942          * @event render
33943          * Fires when the grid is rendered
33944          * @param {Grid} grid
33945          */
33946         'render' : true
33947     });
33948
33949     Roo.grid.Grid.superclass.constructor.call(this);
33950 };
33951 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
33952     
33953     /**
33954          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
33955          */
33956         /**
33957          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
33958          */
33959         /**
33960          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
33961          */
33962         /**
33963          * @cfg {Roo.grid.Store} ds The data store for the grid
33964          */
33965         /**
33966          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
33967          */
33968         /**
33969      * @cfg {String} ddGroup - drag drop group.
33970      */
33971       /**
33972      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
33973      */
33974
33975     /**
33976      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
33977      */
33978     minColumnWidth : 25,
33979
33980     /**
33981      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
33982      * <b>on initial render.</b> It is more efficient to explicitly size the columns
33983      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
33984      */
33985     autoSizeColumns : false,
33986
33987     /**
33988      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
33989      */
33990     autoSizeHeaders : true,
33991
33992     /**
33993      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
33994      */
33995     monitorWindowResize : true,
33996
33997     /**
33998      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
33999      * rows measured to get a columns size. Default is 0 (all rows).
34000      */
34001     maxRowsToMeasure : 0,
34002
34003     /**
34004      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
34005      */
34006     trackMouseOver : true,
34007
34008     /**
34009     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
34010     */
34011       /**
34012     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
34013     */
34014     
34015     /**
34016     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
34017     */
34018     enableDragDrop : false,
34019     
34020     /**
34021     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
34022     */
34023     enableColumnMove : true,
34024     
34025     /**
34026     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
34027     */
34028     enableColumnHide : true,
34029     
34030     /**
34031     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
34032     */
34033     enableRowHeightSync : false,
34034     
34035     /**
34036     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
34037     */
34038     stripeRows : true,
34039     
34040     /**
34041     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
34042     */
34043     autoHeight : false,
34044
34045     /**
34046      * @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.
34047      */
34048     autoExpandColumn : false,
34049
34050     /**
34051     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
34052     * Default is 50.
34053     */
34054     autoExpandMin : 50,
34055
34056     /**
34057     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
34058     */
34059     autoExpandMax : 1000,
34060
34061     /**
34062     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
34063     */
34064     view : null,
34065
34066     /**
34067     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
34068     */
34069     loadMask : false,
34070     /**
34071     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
34072     */
34073     dropTarget: false,
34074     
34075    
34076     
34077     // private
34078     rendered : false,
34079
34080     /**
34081     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
34082     * of a fixed width. Default is false.
34083     */
34084     /**
34085     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
34086     */
34087     
34088     
34089     /**
34090     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
34091     * %0 is replaced with the number of selected rows.
34092     */
34093     ddText : "{0} selected row{1}",
34094     
34095     
34096     /**
34097      * Called once after all setup has been completed and the grid is ready to be rendered.
34098      * @return {Roo.grid.Grid} this
34099      */
34100     render : function()
34101     {
34102         var c = this.container;
34103         // try to detect autoHeight/width mode
34104         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
34105             this.autoHeight = true;
34106         }
34107         var view = this.getView();
34108         view.init(this);
34109
34110         c.on("click", this.onClick, this);
34111         c.on("dblclick", this.onDblClick, this);
34112         c.on("contextmenu", this.onContextMenu, this);
34113         c.on("keydown", this.onKeyDown, this);
34114         if (Roo.isTouch) {
34115             c.on("touchstart", this.onTouchStart, this);
34116         }
34117
34118         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
34119
34120         this.getSelectionModel().init(this);
34121
34122         view.render();
34123
34124         if(this.loadMask){
34125             this.loadMask = new Roo.LoadMask(this.container,
34126                     Roo.apply({store:this.dataSource}, this.loadMask));
34127         }
34128         
34129         
34130         if (this.toolbar && this.toolbar.xtype) {
34131             this.toolbar.container = this.getView().getHeaderPanel(true);
34132             this.toolbar = new Roo.Toolbar(this.toolbar);
34133         }
34134         if (this.footer && this.footer.xtype) {
34135             this.footer.dataSource = this.getDataSource();
34136             this.footer.container = this.getView().getFooterPanel(true);
34137             this.footer = Roo.factory(this.footer, Roo);
34138         }
34139         if (this.dropTarget && this.dropTarget.xtype) {
34140             delete this.dropTarget.xtype;
34141             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
34142         }
34143         
34144         
34145         this.rendered = true;
34146         this.fireEvent('render', this);
34147         return this;
34148     },
34149
34150     /**
34151      * Reconfigures the grid to use a different Store and Column Model.
34152      * The View will be bound to the new objects and refreshed.
34153      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
34154      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
34155      */
34156     reconfigure : function(dataSource, colModel){
34157         if(this.loadMask){
34158             this.loadMask.destroy();
34159             this.loadMask = new Roo.LoadMask(this.container,
34160                     Roo.apply({store:dataSource}, this.loadMask));
34161         }
34162         this.view.bind(dataSource, colModel);
34163         this.dataSource = dataSource;
34164         this.colModel = colModel;
34165         this.view.refresh(true);
34166     },
34167     /**
34168      * addColumns
34169      * Add's a column, default at the end..
34170      
34171      * @param {int} position to add (default end)
34172      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
34173      */
34174     addColumns : function(pos, ar)
34175     {
34176         
34177         for (var i =0;i< ar.length;i++) {
34178             var cfg = ar[i];
34179             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
34180             this.cm.lookup[cfg.id] = cfg;
34181         }
34182         
34183         
34184         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
34185             pos = this.cm.config.length; //this.cm.config.push(cfg);
34186         } 
34187         pos = Math.max(0,pos);
34188         ar.unshift(0);
34189         ar.unshift(pos);
34190         this.cm.config.splice.apply(this.cm.config, ar);
34191         
34192         
34193         
34194         this.view.generateRules(this.cm);
34195         this.view.refresh(true);
34196         
34197     },
34198     
34199     
34200     
34201     
34202     // private
34203     onKeyDown : function(e){
34204         this.fireEvent("keydown", e);
34205     },
34206
34207     /**
34208      * Destroy this grid.
34209      * @param {Boolean} removeEl True to remove the element
34210      */
34211     destroy : function(removeEl, keepListeners){
34212         if(this.loadMask){
34213             this.loadMask.destroy();
34214         }
34215         var c = this.container;
34216         c.removeAllListeners();
34217         this.view.destroy();
34218         this.colModel.purgeListeners();
34219         if(!keepListeners){
34220             this.purgeListeners();
34221         }
34222         c.update("");
34223         if(removeEl === true){
34224             c.remove();
34225         }
34226     },
34227
34228     // private
34229     processEvent : function(name, e){
34230         // does this fire select???
34231         //Roo.log('grid:processEvent '  + name);
34232         
34233         if (name != 'touchstart' ) {
34234             this.fireEvent(name, e);    
34235         }
34236         
34237         var t = e.getTarget();
34238         var v = this.view;
34239         var header = v.findHeaderIndex(t);
34240         if(header !== false){
34241             var ename = name == 'touchstart' ? 'click' : name;
34242              
34243             this.fireEvent("header" + ename, this, header, e);
34244         }else{
34245             var row = v.findRowIndex(t);
34246             var cell = v.findCellIndex(t);
34247             if (name == 'touchstart') {
34248                 // first touch is always a click.
34249                 // hopefull this happens after selection is updated.?
34250                 name = false;
34251                 
34252                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
34253                     var cs = this.selModel.getSelectedCell();
34254                     if (row == cs[0] && cell == cs[1]){
34255                         name = 'dblclick';
34256                     }
34257                 }
34258                 if (typeof(this.selModel.getSelections) != 'undefined') {
34259                     var cs = this.selModel.getSelections();
34260                     var ds = this.dataSource;
34261                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
34262                         name = 'dblclick';
34263                     }
34264                 }
34265                 if (!name) {
34266                     return;
34267                 }
34268             }
34269             
34270             
34271             if(row !== false){
34272                 this.fireEvent("row" + name, this, row, e);
34273                 if(cell !== false){
34274                     this.fireEvent("cell" + name, this, row, cell, e);
34275                 }
34276             }
34277         }
34278     },
34279
34280     // private
34281     onClick : function(e){
34282         this.processEvent("click", e);
34283     },
34284    // private
34285     onTouchStart : function(e){
34286         this.processEvent("touchstart", e);
34287     },
34288
34289     // private
34290     onContextMenu : function(e, t){
34291         this.processEvent("contextmenu", e);
34292     },
34293
34294     // private
34295     onDblClick : function(e){
34296         this.processEvent("dblclick", e);
34297     },
34298
34299     // private
34300     walkCells : function(row, col, step, fn, scope){
34301         var cm = this.colModel, clen = cm.getColumnCount();
34302         var ds = this.dataSource, rlen = ds.getCount(), first = true;
34303         if(step < 0){
34304             if(col < 0){
34305                 row--;
34306                 first = false;
34307             }
34308             while(row >= 0){
34309                 if(!first){
34310                     col = clen-1;
34311                 }
34312                 first = false;
34313                 while(col >= 0){
34314                     if(fn.call(scope || this, row, col, cm) === true){
34315                         return [row, col];
34316                     }
34317                     col--;
34318                 }
34319                 row--;
34320             }
34321         } else {
34322             if(col >= clen){
34323                 row++;
34324                 first = false;
34325             }
34326             while(row < rlen){
34327                 if(!first){
34328                     col = 0;
34329                 }
34330                 first = false;
34331                 while(col < clen){
34332                     if(fn.call(scope || this, row, col, cm) === true){
34333                         return [row, col];
34334                     }
34335                     col++;
34336                 }
34337                 row++;
34338             }
34339         }
34340         return null;
34341     },
34342
34343     // private
34344     getSelections : function(){
34345         return this.selModel.getSelections();
34346     },
34347
34348     /**
34349      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
34350      * but if manual update is required this method will initiate it.
34351      */
34352     autoSize : function(){
34353         if(this.rendered){
34354             this.view.layout();
34355             if(this.view.adjustForScroll){
34356                 this.view.adjustForScroll();
34357             }
34358         }
34359     },
34360
34361     /**
34362      * Returns the grid's underlying element.
34363      * @return {Element} The element
34364      */
34365     getGridEl : function(){
34366         return this.container;
34367     },
34368
34369     // private for compatibility, overridden by editor grid
34370     stopEditing : function(){},
34371
34372     /**
34373      * Returns the grid's SelectionModel.
34374      * @return {SelectionModel}
34375      */
34376     getSelectionModel : function(){
34377         if(!this.selModel){
34378             this.selModel = new Roo.grid.RowSelectionModel();
34379         }
34380         return this.selModel;
34381     },
34382
34383     /**
34384      * Returns the grid's DataSource.
34385      * @return {DataSource}
34386      */
34387     getDataSource : function(){
34388         return this.dataSource;
34389     },
34390
34391     /**
34392      * Returns the grid's ColumnModel.
34393      * @return {ColumnModel}
34394      */
34395     getColumnModel : function(){
34396         return this.colModel;
34397     },
34398
34399     /**
34400      * Returns the grid's GridView object.
34401      * @return {GridView}
34402      */
34403     getView : function(){
34404         if(!this.view){
34405             this.view = new Roo.grid.GridView(this.viewConfig);
34406             this.relayEvents(this.view, [
34407                 "beforerowremoved", "beforerowsinserted",
34408                 "beforerefresh", "rowremoved",
34409                 "rowsinserted", "rowupdated" ,"refresh"
34410             ]);
34411         }
34412         return this.view;
34413     },
34414     /**
34415      * Called to get grid's drag proxy text, by default returns this.ddText.
34416      * Override this to put something different in the dragged text.
34417      * @return {String}
34418      */
34419     getDragDropText : function(){
34420         var count = this.selModel.getCount();
34421         return String.format(this.ddText, count, count == 1 ? '' : 's');
34422     }
34423 });
34424 /*
34425  * Based on:
34426  * Ext JS Library 1.1.1
34427  * Copyright(c) 2006-2007, Ext JS, LLC.
34428  *
34429  * Originally Released Under LGPL - original licence link has changed is not relivant.
34430  *
34431  * Fork - LGPL
34432  * <script type="text/javascript">
34433  */
34434  /**
34435  * @class Roo.grid.AbstractGridView
34436  * @extends Roo.util.Observable
34437  * @abstract
34438  * Abstract base class for grid Views
34439  * @constructor
34440  */
34441 Roo.grid.AbstractGridView = function(){
34442         this.grid = null;
34443         
34444         this.events = {
34445             "beforerowremoved" : true,
34446             "beforerowsinserted" : true,
34447             "beforerefresh" : true,
34448             "rowremoved" : true,
34449             "rowsinserted" : true,
34450             "rowupdated" : true,
34451             "refresh" : true
34452         };
34453     Roo.grid.AbstractGridView.superclass.constructor.call(this);
34454 };
34455
34456 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
34457     rowClass : "x-grid-row",
34458     cellClass : "x-grid-cell",
34459     tdClass : "x-grid-td",
34460     hdClass : "x-grid-hd",
34461     splitClass : "x-grid-hd-split",
34462     
34463     init: function(grid){
34464         this.grid = grid;
34465                 var cid = this.grid.getGridEl().id;
34466         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
34467         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
34468         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
34469         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
34470         },
34471         
34472     getColumnRenderers : function(){
34473         var renderers = [];
34474         var cm = this.grid.colModel;
34475         var colCount = cm.getColumnCount();
34476         for(var i = 0; i < colCount; i++){
34477             renderers[i] = cm.getRenderer(i);
34478         }
34479         return renderers;
34480     },
34481     
34482     getColumnIds : function(){
34483         var ids = [];
34484         var cm = this.grid.colModel;
34485         var colCount = cm.getColumnCount();
34486         for(var i = 0; i < colCount; i++){
34487             ids[i] = cm.getColumnId(i);
34488         }
34489         return ids;
34490     },
34491     
34492     getDataIndexes : function(){
34493         if(!this.indexMap){
34494             this.indexMap = this.buildIndexMap();
34495         }
34496         return this.indexMap.colToData;
34497     },
34498     
34499     getColumnIndexByDataIndex : function(dataIndex){
34500         if(!this.indexMap){
34501             this.indexMap = this.buildIndexMap();
34502         }
34503         return this.indexMap.dataToCol[dataIndex];
34504     },
34505     
34506     /**
34507      * Set a css style for a column dynamically. 
34508      * @param {Number} colIndex The index of the column
34509      * @param {String} name The css property name
34510      * @param {String} value The css value
34511      */
34512     setCSSStyle : function(colIndex, name, value){
34513         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
34514         Roo.util.CSS.updateRule(selector, name, value);
34515     },
34516     
34517     generateRules : function(cm){
34518         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
34519         Roo.util.CSS.removeStyleSheet(rulesId);
34520         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34521             var cid = cm.getColumnId(i);
34522             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
34523                          this.tdSelector, cid, " {\n}\n",
34524                          this.hdSelector, cid, " {\n}\n",
34525                          this.splitSelector, cid, " {\n}\n");
34526         }
34527         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34528     }
34529 });/*
34530  * Based on:
34531  * Ext JS Library 1.1.1
34532  * Copyright(c) 2006-2007, Ext JS, LLC.
34533  *
34534  * Originally Released Under LGPL - original licence link has changed is not relivant.
34535  *
34536  * Fork - LGPL
34537  * <script type="text/javascript">
34538  */
34539
34540 // private
34541 // This is a support class used internally by the Grid components
34542 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
34543     this.grid = grid;
34544     this.view = grid.getView();
34545     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34546     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
34547     if(hd2){
34548         this.setHandleElId(Roo.id(hd));
34549         this.setOuterHandleElId(Roo.id(hd2));
34550     }
34551     this.scroll = false;
34552 };
34553 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
34554     maxDragWidth: 120,
34555     getDragData : function(e){
34556         var t = Roo.lib.Event.getTarget(e);
34557         var h = this.view.findHeaderCell(t);
34558         if(h){
34559             return {ddel: h.firstChild, header:h};
34560         }
34561         return false;
34562     },
34563
34564     onInitDrag : function(e){
34565         this.view.headersDisabled = true;
34566         var clone = this.dragData.ddel.cloneNode(true);
34567         clone.id = Roo.id();
34568         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
34569         this.proxy.update(clone);
34570         return true;
34571     },
34572
34573     afterValidDrop : function(){
34574         var v = this.view;
34575         setTimeout(function(){
34576             v.headersDisabled = false;
34577         }, 50);
34578     },
34579
34580     afterInvalidDrop : function(){
34581         var v = this.view;
34582         setTimeout(function(){
34583             v.headersDisabled = false;
34584         }, 50);
34585     }
34586 });
34587 /*
34588  * Based on:
34589  * Ext JS Library 1.1.1
34590  * Copyright(c) 2006-2007, Ext JS, LLC.
34591  *
34592  * Originally Released Under LGPL - original licence link has changed is not relivant.
34593  *
34594  * Fork - LGPL
34595  * <script type="text/javascript">
34596  */
34597 // private
34598 // This is a support class used internally by the Grid components
34599 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
34600     this.grid = grid;
34601     this.view = grid.getView();
34602     // split the proxies so they don't interfere with mouse events
34603     this.proxyTop = Roo.DomHelper.append(document.body, {
34604         cls:"col-move-top", html:"&#160;"
34605     }, true);
34606     this.proxyBottom = Roo.DomHelper.append(document.body, {
34607         cls:"col-move-bottom", html:"&#160;"
34608     }, true);
34609     this.proxyTop.hide = this.proxyBottom.hide = function(){
34610         this.setLeftTop(-100,-100);
34611         this.setStyle("visibility", "hidden");
34612     };
34613     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34614     // temporarily disabled
34615     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
34616     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
34617 };
34618 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
34619     proxyOffsets : [-4, -9],
34620     fly: Roo.Element.fly,
34621
34622     getTargetFromEvent : function(e){
34623         var t = Roo.lib.Event.getTarget(e);
34624         var cindex = this.view.findCellIndex(t);
34625         if(cindex !== false){
34626             return this.view.getHeaderCell(cindex);
34627         }
34628         return null;
34629     },
34630
34631     nextVisible : function(h){
34632         var v = this.view, cm = this.grid.colModel;
34633         h = h.nextSibling;
34634         while(h){
34635             if(!cm.isHidden(v.getCellIndex(h))){
34636                 return h;
34637             }
34638             h = h.nextSibling;
34639         }
34640         return null;
34641     },
34642
34643     prevVisible : function(h){
34644         var v = this.view, cm = this.grid.colModel;
34645         h = h.prevSibling;
34646         while(h){
34647             if(!cm.isHidden(v.getCellIndex(h))){
34648                 return h;
34649             }
34650             h = h.prevSibling;
34651         }
34652         return null;
34653     },
34654
34655     positionIndicator : function(h, n, e){
34656         var x = Roo.lib.Event.getPageX(e);
34657         var r = Roo.lib.Dom.getRegion(n.firstChild);
34658         var px, pt, py = r.top + this.proxyOffsets[1];
34659         if((r.right - x) <= (r.right-r.left)/2){
34660             px = r.right+this.view.borderWidth;
34661             pt = "after";
34662         }else{
34663             px = r.left;
34664             pt = "before";
34665         }
34666         var oldIndex = this.view.getCellIndex(h);
34667         var newIndex = this.view.getCellIndex(n);
34668
34669         if(this.grid.colModel.isFixed(newIndex)){
34670             return false;
34671         }
34672
34673         var locked = this.grid.colModel.isLocked(newIndex);
34674
34675         if(pt == "after"){
34676             newIndex++;
34677         }
34678         if(oldIndex < newIndex){
34679             newIndex--;
34680         }
34681         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
34682             return false;
34683         }
34684         px +=  this.proxyOffsets[0];
34685         this.proxyTop.setLeftTop(px, py);
34686         this.proxyTop.show();
34687         if(!this.bottomOffset){
34688             this.bottomOffset = this.view.mainHd.getHeight();
34689         }
34690         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
34691         this.proxyBottom.show();
34692         return pt;
34693     },
34694
34695     onNodeEnter : function(n, dd, e, data){
34696         if(data.header != n){
34697             this.positionIndicator(data.header, n, e);
34698         }
34699     },
34700
34701     onNodeOver : function(n, dd, e, data){
34702         var result = false;
34703         if(data.header != n){
34704             result = this.positionIndicator(data.header, n, e);
34705         }
34706         if(!result){
34707             this.proxyTop.hide();
34708             this.proxyBottom.hide();
34709         }
34710         return result ? this.dropAllowed : this.dropNotAllowed;
34711     },
34712
34713     onNodeOut : function(n, dd, e, data){
34714         this.proxyTop.hide();
34715         this.proxyBottom.hide();
34716     },
34717
34718     onNodeDrop : function(n, dd, e, data){
34719         var h = data.header;
34720         if(h != n){
34721             var cm = this.grid.colModel;
34722             var x = Roo.lib.Event.getPageX(e);
34723             var r = Roo.lib.Dom.getRegion(n.firstChild);
34724             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
34725             var oldIndex = this.view.getCellIndex(h);
34726             var newIndex = this.view.getCellIndex(n);
34727             var locked = cm.isLocked(newIndex);
34728             if(pt == "after"){
34729                 newIndex++;
34730             }
34731             if(oldIndex < newIndex){
34732                 newIndex--;
34733             }
34734             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
34735                 return false;
34736             }
34737             cm.setLocked(oldIndex, locked, true);
34738             cm.moveColumn(oldIndex, newIndex);
34739             this.grid.fireEvent("columnmove", oldIndex, newIndex);
34740             return true;
34741         }
34742         return false;
34743     }
34744 });
34745 /*
34746  * Based on:
34747  * Ext JS Library 1.1.1
34748  * Copyright(c) 2006-2007, Ext JS, LLC.
34749  *
34750  * Originally Released Under LGPL - original licence link has changed is not relivant.
34751  *
34752  * Fork - LGPL
34753  * <script type="text/javascript">
34754  */
34755   
34756 /**
34757  * @class Roo.grid.GridView
34758  * @extends Roo.util.Observable
34759  *
34760  * @constructor
34761  * @param {Object} config
34762  */
34763 Roo.grid.GridView = function(config){
34764     Roo.grid.GridView.superclass.constructor.call(this);
34765     this.el = null;
34766
34767     Roo.apply(this, config);
34768 };
34769
34770 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
34771
34772     unselectable :  'unselectable="on"',
34773     unselectableCls :  'x-unselectable',
34774     
34775     
34776     rowClass : "x-grid-row",
34777
34778     cellClass : "x-grid-col",
34779
34780     tdClass : "x-grid-td",
34781
34782     hdClass : "x-grid-hd",
34783
34784     splitClass : "x-grid-split",
34785
34786     sortClasses : ["sort-asc", "sort-desc"],
34787
34788     enableMoveAnim : false,
34789
34790     hlColor: "C3DAF9",
34791
34792     dh : Roo.DomHelper,
34793
34794     fly : Roo.Element.fly,
34795
34796     css : Roo.util.CSS,
34797
34798     borderWidth: 1,
34799
34800     splitOffset: 3,
34801
34802     scrollIncrement : 22,
34803
34804     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
34805
34806     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
34807
34808     bind : function(ds, cm){
34809         if(this.ds){
34810             this.ds.un("load", this.onLoad, this);
34811             this.ds.un("datachanged", this.onDataChange, this);
34812             this.ds.un("add", this.onAdd, this);
34813             this.ds.un("remove", this.onRemove, this);
34814             this.ds.un("update", this.onUpdate, this);
34815             this.ds.un("clear", this.onClear, this);
34816         }
34817         if(ds){
34818             ds.on("load", this.onLoad, this);
34819             ds.on("datachanged", this.onDataChange, this);
34820             ds.on("add", this.onAdd, this);
34821             ds.on("remove", this.onRemove, this);
34822             ds.on("update", this.onUpdate, this);
34823             ds.on("clear", this.onClear, this);
34824         }
34825         this.ds = ds;
34826
34827         if(this.cm){
34828             this.cm.un("widthchange", this.onColWidthChange, this);
34829             this.cm.un("headerchange", this.onHeaderChange, this);
34830             this.cm.un("hiddenchange", this.onHiddenChange, this);
34831             this.cm.un("columnmoved", this.onColumnMove, this);
34832             this.cm.un("columnlockchange", this.onColumnLock, this);
34833         }
34834         if(cm){
34835             this.generateRules(cm);
34836             cm.on("widthchange", this.onColWidthChange, this);
34837             cm.on("headerchange", this.onHeaderChange, this);
34838             cm.on("hiddenchange", this.onHiddenChange, this);
34839             cm.on("columnmoved", this.onColumnMove, this);
34840             cm.on("columnlockchange", this.onColumnLock, this);
34841         }
34842         this.cm = cm;
34843     },
34844
34845     init: function(grid){
34846         Roo.grid.GridView.superclass.init.call(this, grid);
34847
34848         this.bind(grid.dataSource, grid.colModel);
34849
34850         grid.on("headerclick", this.handleHeaderClick, this);
34851
34852         if(grid.trackMouseOver){
34853             grid.on("mouseover", this.onRowOver, this);
34854             grid.on("mouseout", this.onRowOut, this);
34855         }
34856         grid.cancelTextSelection = function(){};
34857         this.gridId = grid.id;
34858
34859         var tpls = this.templates || {};
34860
34861         if(!tpls.master){
34862             tpls.master = new Roo.Template(
34863                '<div class="x-grid" hidefocus="true">',
34864                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
34865                   '<div class="x-grid-topbar"></div>',
34866                   '<div class="x-grid-scroller"><div></div></div>',
34867                   '<div class="x-grid-locked">',
34868                       '<div class="x-grid-header">{lockedHeader}</div>',
34869                       '<div class="x-grid-body">{lockedBody}</div>',
34870                   "</div>",
34871                   '<div class="x-grid-viewport">',
34872                       '<div class="x-grid-header">{header}</div>',
34873                       '<div class="x-grid-body">{body}</div>',
34874                   "</div>",
34875                   '<div class="x-grid-bottombar"></div>',
34876                  
34877                   '<div class="x-grid-resize-proxy">&#160;</div>',
34878                "</div>"
34879             );
34880             tpls.master.disableformats = true;
34881         }
34882
34883         if(!tpls.header){
34884             tpls.header = new Roo.Template(
34885                '<table border="0" cellspacing="0" cellpadding="0">',
34886                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
34887                "</table>{splits}"
34888             );
34889             tpls.header.disableformats = true;
34890         }
34891         tpls.header.compile();
34892
34893         if(!tpls.hcell){
34894             tpls.hcell = new Roo.Template(
34895                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
34896                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
34897                 "</div></td>"
34898              );
34899              tpls.hcell.disableFormats = true;
34900         }
34901         tpls.hcell.compile();
34902
34903         if(!tpls.hsplit){
34904             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
34905                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
34906             tpls.hsplit.disableFormats = true;
34907         }
34908         tpls.hsplit.compile();
34909
34910         if(!tpls.body){
34911             tpls.body = new Roo.Template(
34912                '<table border="0" cellspacing="0" cellpadding="0">',
34913                "<tbody>{rows}</tbody>",
34914                "</table>"
34915             );
34916             tpls.body.disableFormats = true;
34917         }
34918         tpls.body.compile();
34919
34920         if(!tpls.row){
34921             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
34922             tpls.row.disableFormats = true;
34923         }
34924         tpls.row.compile();
34925
34926         if(!tpls.cell){
34927             tpls.cell = new Roo.Template(
34928                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
34929                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
34930                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
34931                 "</td>"
34932             );
34933             tpls.cell.disableFormats = true;
34934         }
34935         tpls.cell.compile();
34936
34937         this.templates = tpls;
34938     },
34939
34940     // remap these for backwards compat
34941     onColWidthChange : function(){
34942         this.updateColumns.apply(this, arguments);
34943     },
34944     onHeaderChange : function(){
34945         this.updateHeaders.apply(this, arguments);
34946     }, 
34947     onHiddenChange : function(){
34948         this.handleHiddenChange.apply(this, arguments);
34949     },
34950     onColumnMove : function(){
34951         this.handleColumnMove.apply(this, arguments);
34952     },
34953     onColumnLock : function(){
34954         this.handleLockChange.apply(this, arguments);
34955     },
34956
34957     onDataChange : function(){
34958         this.refresh();
34959         this.updateHeaderSortState();
34960     },
34961
34962     onClear : function(){
34963         this.refresh();
34964     },
34965
34966     onUpdate : function(ds, record){
34967         this.refreshRow(record);
34968     },
34969
34970     refreshRow : function(record){
34971         var ds = this.ds, index;
34972         if(typeof record == 'number'){
34973             index = record;
34974             record = ds.getAt(index);
34975         }else{
34976             index = ds.indexOf(record);
34977         }
34978         this.insertRows(ds, index, index, true);
34979         this.onRemove(ds, record, index+1, true);
34980         this.syncRowHeights(index, index);
34981         this.layout();
34982         this.fireEvent("rowupdated", this, index, record);
34983     },
34984
34985     onAdd : function(ds, records, index){
34986         this.insertRows(ds, index, index + (records.length-1));
34987     },
34988
34989     onRemove : function(ds, record, index, isUpdate){
34990         if(isUpdate !== true){
34991             this.fireEvent("beforerowremoved", this, index, record);
34992         }
34993         var bt = this.getBodyTable(), lt = this.getLockedTable();
34994         if(bt.rows[index]){
34995             bt.firstChild.removeChild(bt.rows[index]);
34996         }
34997         if(lt.rows[index]){
34998             lt.firstChild.removeChild(lt.rows[index]);
34999         }
35000         if(isUpdate !== true){
35001             this.stripeRows(index);
35002             this.syncRowHeights(index, index);
35003             this.layout();
35004             this.fireEvent("rowremoved", this, index, record);
35005         }
35006     },
35007
35008     onLoad : function(){
35009         this.scrollToTop();
35010     },
35011
35012     /**
35013      * Scrolls the grid to the top
35014      */
35015     scrollToTop : function(){
35016         if(this.scroller){
35017             this.scroller.dom.scrollTop = 0;
35018             this.syncScroll();
35019         }
35020     },
35021
35022     /**
35023      * Gets a panel in the header of the grid that can be used for toolbars etc.
35024      * After modifying the contents of this panel a call to grid.autoSize() may be
35025      * required to register any changes in size.
35026      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
35027      * @return Roo.Element
35028      */
35029     getHeaderPanel : function(doShow){
35030         if(doShow){
35031             this.headerPanel.show();
35032         }
35033         return this.headerPanel;
35034     },
35035
35036     /**
35037      * Gets a panel in the footer of the grid that can be used for toolbars etc.
35038      * After modifying the contents of this panel a call to grid.autoSize() may be
35039      * required to register any changes in size.
35040      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
35041      * @return Roo.Element
35042      */
35043     getFooterPanel : function(doShow){
35044         if(doShow){
35045             this.footerPanel.show();
35046         }
35047         return this.footerPanel;
35048     },
35049
35050     initElements : function(){
35051         var E = Roo.Element;
35052         var el = this.grid.getGridEl().dom.firstChild;
35053         var cs = el.childNodes;
35054
35055         this.el = new E(el);
35056         
35057          this.focusEl = new E(el.firstChild);
35058         this.focusEl.swallowEvent("click", true);
35059         
35060         this.headerPanel = new E(cs[1]);
35061         this.headerPanel.enableDisplayMode("block");
35062
35063         this.scroller = new E(cs[2]);
35064         this.scrollSizer = new E(this.scroller.dom.firstChild);
35065
35066         this.lockedWrap = new E(cs[3]);
35067         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
35068         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
35069
35070         this.mainWrap = new E(cs[4]);
35071         this.mainHd = new E(this.mainWrap.dom.firstChild);
35072         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
35073
35074         this.footerPanel = new E(cs[5]);
35075         this.footerPanel.enableDisplayMode("block");
35076
35077         this.resizeProxy = new E(cs[6]);
35078
35079         this.headerSelector = String.format(
35080            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
35081            this.lockedHd.id, this.mainHd.id
35082         );
35083
35084         this.splitterSelector = String.format(
35085            '#{0} div.x-grid-split, #{1} div.x-grid-split',
35086            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
35087         );
35088     },
35089     idToCssName : function(s)
35090     {
35091         return s.replace(/[^a-z0-9]+/ig, '-');
35092     },
35093
35094     getHeaderCell : function(index){
35095         return Roo.DomQuery.select(this.headerSelector)[index];
35096     },
35097
35098     getHeaderCellMeasure : function(index){
35099         return this.getHeaderCell(index).firstChild;
35100     },
35101
35102     getHeaderCellText : function(index){
35103         return this.getHeaderCell(index).firstChild.firstChild;
35104     },
35105
35106     getLockedTable : function(){
35107         return this.lockedBody.dom.firstChild;
35108     },
35109
35110     getBodyTable : function(){
35111         return this.mainBody.dom.firstChild;
35112     },
35113
35114     getLockedRow : function(index){
35115         return this.getLockedTable().rows[index];
35116     },
35117
35118     getRow : function(index){
35119         return this.getBodyTable().rows[index];
35120     },
35121
35122     getRowComposite : function(index){
35123         if(!this.rowEl){
35124             this.rowEl = new Roo.CompositeElementLite();
35125         }
35126         var els = [], lrow, mrow;
35127         if(lrow = this.getLockedRow(index)){
35128             els.push(lrow);
35129         }
35130         if(mrow = this.getRow(index)){
35131             els.push(mrow);
35132         }
35133         this.rowEl.elements = els;
35134         return this.rowEl;
35135     },
35136     /**
35137      * Gets the 'td' of the cell
35138      * 
35139      * @param {Integer} rowIndex row to select
35140      * @param {Integer} colIndex column to select
35141      * 
35142      * @return {Object} 
35143      */
35144     getCell : function(rowIndex, colIndex){
35145         var locked = this.cm.getLockedCount();
35146         var source;
35147         if(colIndex < locked){
35148             source = this.lockedBody.dom.firstChild;
35149         }else{
35150             source = this.mainBody.dom.firstChild;
35151             colIndex -= locked;
35152         }
35153         return source.rows[rowIndex].childNodes[colIndex];
35154     },
35155
35156     getCellText : function(rowIndex, colIndex){
35157         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
35158     },
35159
35160     getCellBox : function(cell){
35161         var b = this.fly(cell).getBox();
35162         if(Roo.isOpera){ // opera fails to report the Y
35163             b.y = cell.offsetTop + this.mainBody.getY();
35164         }
35165         return b;
35166     },
35167
35168     getCellIndex : function(cell){
35169         var id = String(cell.className).match(this.cellRE);
35170         if(id){
35171             return parseInt(id[1], 10);
35172         }
35173         return 0;
35174     },
35175
35176     findHeaderIndex : function(n){
35177         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35178         return r ? this.getCellIndex(r) : false;
35179     },
35180
35181     findHeaderCell : function(n){
35182         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35183         return r ? r : false;
35184     },
35185
35186     findRowIndex : function(n){
35187         if(!n){
35188             return false;
35189         }
35190         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
35191         return r ? r.rowIndex : false;
35192     },
35193
35194     findCellIndex : function(node){
35195         var stop = this.el.dom;
35196         while(node && node != stop){
35197             if(this.findRE.test(node.className)){
35198                 return this.getCellIndex(node);
35199             }
35200             node = node.parentNode;
35201         }
35202         return false;
35203     },
35204
35205     getColumnId : function(index){
35206         return this.cm.getColumnId(index);
35207     },
35208
35209     getSplitters : function()
35210     {
35211         if(this.splitterSelector){
35212            return Roo.DomQuery.select(this.splitterSelector);
35213         }else{
35214             return null;
35215       }
35216     },
35217
35218     getSplitter : function(index){
35219         return this.getSplitters()[index];
35220     },
35221
35222     onRowOver : function(e, t){
35223         var row;
35224         if((row = this.findRowIndex(t)) !== false){
35225             this.getRowComposite(row).addClass("x-grid-row-over");
35226         }
35227     },
35228
35229     onRowOut : function(e, t){
35230         var row;
35231         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
35232             this.getRowComposite(row).removeClass("x-grid-row-over");
35233         }
35234     },
35235
35236     renderHeaders : function(){
35237         var cm = this.cm;
35238         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
35239         var cb = [], lb = [], sb = [], lsb = [], p = {};
35240         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35241             p.cellId = "x-grid-hd-0-" + i;
35242             p.splitId = "x-grid-csplit-0-" + i;
35243             p.id = cm.getColumnId(i);
35244             p.value = cm.getColumnHeader(i) || "";
35245             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
35246             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
35247             if(!cm.isLocked(i)){
35248                 cb[cb.length] = ct.apply(p);
35249                 sb[sb.length] = st.apply(p);
35250             }else{
35251                 lb[lb.length] = ct.apply(p);
35252                 lsb[lsb.length] = st.apply(p);
35253             }
35254         }
35255         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
35256                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
35257     },
35258
35259     updateHeaders : function(){
35260         var html = this.renderHeaders();
35261         this.lockedHd.update(html[0]);
35262         this.mainHd.update(html[1]);
35263     },
35264
35265     /**
35266      * Focuses the specified row.
35267      * @param {Number} row The row index
35268      */
35269     focusRow : function(row)
35270     {
35271         //Roo.log('GridView.focusRow');
35272         var x = this.scroller.dom.scrollLeft;
35273         this.focusCell(row, 0, false);
35274         this.scroller.dom.scrollLeft = x;
35275     },
35276
35277     /**
35278      * Focuses the specified cell.
35279      * @param {Number} row The row index
35280      * @param {Number} col The column index
35281      * @param {Boolean} hscroll false to disable horizontal scrolling
35282      */
35283     focusCell : function(row, col, hscroll)
35284     {
35285         //Roo.log('GridView.focusCell');
35286         var el = this.ensureVisible(row, col, hscroll);
35287         this.focusEl.alignTo(el, "tl-tl");
35288         if(Roo.isGecko){
35289             this.focusEl.focus();
35290         }else{
35291             this.focusEl.focus.defer(1, this.focusEl);
35292         }
35293     },
35294
35295     /**
35296      * Scrolls the specified cell into view
35297      * @param {Number} row The row index
35298      * @param {Number} col The column index
35299      * @param {Boolean} hscroll false to disable horizontal scrolling
35300      */
35301     ensureVisible : function(row, col, hscroll)
35302     {
35303         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
35304         //return null; //disable for testing.
35305         if(typeof row != "number"){
35306             row = row.rowIndex;
35307         }
35308         if(row < 0 && row >= this.ds.getCount()){
35309             return  null;
35310         }
35311         col = (col !== undefined ? col : 0);
35312         var cm = this.grid.colModel;
35313         while(cm.isHidden(col)){
35314             col++;
35315         }
35316
35317         var el = this.getCell(row, col);
35318         if(!el){
35319             return null;
35320         }
35321         var c = this.scroller.dom;
35322
35323         var ctop = parseInt(el.offsetTop, 10);
35324         var cleft = parseInt(el.offsetLeft, 10);
35325         var cbot = ctop + el.offsetHeight;
35326         var cright = cleft + el.offsetWidth;
35327         
35328         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
35329         var stop = parseInt(c.scrollTop, 10);
35330         var sleft = parseInt(c.scrollLeft, 10);
35331         var sbot = stop + ch;
35332         var sright = sleft + c.clientWidth;
35333         /*
35334         Roo.log('GridView.ensureVisible:' +
35335                 ' ctop:' + ctop +
35336                 ' c.clientHeight:' + c.clientHeight +
35337                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
35338                 ' stop:' + stop +
35339                 ' cbot:' + cbot +
35340                 ' sbot:' + sbot +
35341                 ' ch:' + ch  
35342                 );
35343         */
35344         if(ctop < stop){
35345             c.scrollTop = ctop;
35346             //Roo.log("set scrolltop to ctop DISABLE?");
35347         }else if(cbot > sbot){
35348             //Roo.log("set scrolltop to cbot-ch");
35349             c.scrollTop = cbot-ch;
35350         }
35351         
35352         if(hscroll !== false){
35353             if(cleft < sleft){
35354                 c.scrollLeft = cleft;
35355             }else if(cright > sright){
35356                 c.scrollLeft = cright-c.clientWidth;
35357             }
35358         }
35359          
35360         return el;
35361     },
35362
35363     updateColumns : function(){
35364         this.grid.stopEditing();
35365         var cm = this.grid.colModel, colIds = this.getColumnIds();
35366         //var totalWidth = cm.getTotalWidth();
35367         var pos = 0;
35368         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35369             //if(cm.isHidden(i)) continue;
35370             var w = cm.getColumnWidth(i);
35371             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35372             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35373         }
35374         this.updateSplitters();
35375     },
35376
35377     generateRules : function(cm){
35378         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
35379         Roo.util.CSS.removeStyleSheet(rulesId);
35380         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35381             var cid = cm.getColumnId(i);
35382             var align = '';
35383             if(cm.config[i].align){
35384                 align = 'text-align:'+cm.config[i].align+';';
35385             }
35386             var hidden = '';
35387             if(cm.isHidden(i)){
35388                 hidden = 'display:none;';
35389             }
35390             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
35391             ruleBuf.push(
35392                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
35393                     this.hdSelector, cid, " {\n", align, width, "}\n",
35394                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
35395                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
35396         }
35397         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
35398     },
35399
35400     updateSplitters : function(){
35401         var cm = this.cm, s = this.getSplitters();
35402         if(s){ // splitters not created yet
35403             var pos = 0, locked = true;
35404             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35405                 if(cm.isHidden(i)) {
35406                     continue;
35407                 }
35408                 var w = cm.getColumnWidth(i); // make sure it's a number
35409                 if(!cm.isLocked(i) && locked){
35410                     pos = 0;
35411                     locked = false;
35412                 }
35413                 pos += w;
35414                 s[i].style.left = (pos-this.splitOffset) + "px";
35415             }
35416         }
35417     },
35418
35419     handleHiddenChange : function(colModel, colIndex, hidden){
35420         if(hidden){
35421             this.hideColumn(colIndex);
35422         }else{
35423             this.unhideColumn(colIndex);
35424         }
35425     },
35426
35427     hideColumn : function(colIndex){
35428         var cid = this.getColumnId(colIndex);
35429         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
35430         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
35431         if(Roo.isSafari){
35432             this.updateHeaders();
35433         }
35434         this.updateSplitters();
35435         this.layout();
35436     },
35437
35438     unhideColumn : function(colIndex){
35439         var cid = this.getColumnId(colIndex);
35440         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
35441         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
35442
35443         if(Roo.isSafari){
35444             this.updateHeaders();
35445         }
35446         this.updateSplitters();
35447         this.layout();
35448     },
35449
35450     insertRows : function(dm, firstRow, lastRow, isUpdate){
35451         if(firstRow == 0 && lastRow == dm.getCount()-1){
35452             this.refresh();
35453         }else{
35454             if(!isUpdate){
35455                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
35456             }
35457             var s = this.getScrollState();
35458             var markup = this.renderRows(firstRow, lastRow);
35459             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
35460             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
35461             this.restoreScroll(s);
35462             if(!isUpdate){
35463                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
35464                 this.syncRowHeights(firstRow, lastRow);
35465                 this.stripeRows(firstRow);
35466                 this.layout();
35467             }
35468         }
35469     },
35470
35471     bufferRows : function(markup, target, index){
35472         var before = null, trows = target.rows, tbody = target.tBodies[0];
35473         if(index < trows.length){
35474             before = trows[index];
35475         }
35476         var b = document.createElement("div");
35477         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
35478         var rows = b.firstChild.rows;
35479         for(var i = 0, len = rows.length; i < len; i++){
35480             if(before){
35481                 tbody.insertBefore(rows[0], before);
35482             }else{
35483                 tbody.appendChild(rows[0]);
35484             }
35485         }
35486         b.innerHTML = "";
35487         b = null;
35488     },
35489
35490     deleteRows : function(dm, firstRow, lastRow){
35491         if(dm.getRowCount()<1){
35492             this.fireEvent("beforerefresh", this);
35493             this.mainBody.update("");
35494             this.lockedBody.update("");
35495             this.fireEvent("refresh", this);
35496         }else{
35497             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
35498             var bt = this.getBodyTable();
35499             var tbody = bt.firstChild;
35500             var rows = bt.rows;
35501             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
35502                 tbody.removeChild(rows[firstRow]);
35503             }
35504             this.stripeRows(firstRow);
35505             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
35506         }
35507     },
35508
35509     updateRows : function(dataSource, firstRow, lastRow){
35510         var s = this.getScrollState();
35511         this.refresh();
35512         this.restoreScroll(s);
35513     },
35514
35515     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
35516         if(!noRefresh){
35517            this.refresh();
35518         }
35519         this.updateHeaderSortState();
35520     },
35521
35522     getScrollState : function(){
35523         
35524         var sb = this.scroller.dom;
35525         return {left: sb.scrollLeft, top: sb.scrollTop};
35526     },
35527
35528     stripeRows : function(startRow){
35529         if(!this.grid.stripeRows || this.ds.getCount() < 1){
35530             return;
35531         }
35532         startRow = startRow || 0;
35533         var rows = this.getBodyTable().rows;
35534         var lrows = this.getLockedTable().rows;
35535         var cls = ' x-grid-row-alt ';
35536         for(var i = startRow, len = rows.length; i < len; i++){
35537             var row = rows[i], lrow = lrows[i];
35538             var isAlt = ((i+1) % 2 == 0);
35539             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
35540             if(isAlt == hasAlt){
35541                 continue;
35542             }
35543             if(isAlt){
35544                 row.className += " x-grid-row-alt";
35545             }else{
35546                 row.className = row.className.replace("x-grid-row-alt", "");
35547             }
35548             if(lrow){
35549                 lrow.className = row.className;
35550             }
35551         }
35552     },
35553
35554     restoreScroll : function(state){
35555         //Roo.log('GridView.restoreScroll');
35556         var sb = this.scroller.dom;
35557         sb.scrollLeft = state.left;
35558         sb.scrollTop = state.top;
35559         this.syncScroll();
35560     },
35561
35562     syncScroll : function(){
35563         //Roo.log('GridView.syncScroll');
35564         var sb = this.scroller.dom;
35565         var sh = this.mainHd.dom;
35566         var bs = this.mainBody.dom;
35567         var lv = this.lockedBody.dom;
35568         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
35569         lv.scrollTop = bs.scrollTop = sb.scrollTop;
35570     },
35571
35572     handleScroll : function(e){
35573         this.syncScroll();
35574         var sb = this.scroller.dom;
35575         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
35576         e.stopEvent();
35577     },
35578
35579     handleWheel : function(e){
35580         var d = e.getWheelDelta();
35581         this.scroller.dom.scrollTop -= d*22;
35582         // set this here to prevent jumpy scrolling on large tables
35583         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
35584         e.stopEvent();
35585     },
35586
35587     renderRows : function(startRow, endRow){
35588         // pull in all the crap needed to render rows
35589         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
35590         var colCount = cm.getColumnCount();
35591
35592         if(ds.getCount() < 1){
35593             return ["", ""];
35594         }
35595
35596         // build a map for all the columns
35597         var cs = [];
35598         for(var i = 0; i < colCount; i++){
35599             var name = cm.getDataIndex(i);
35600             cs[i] = {
35601                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
35602                 renderer : cm.getRenderer(i),
35603                 id : cm.getColumnId(i),
35604                 locked : cm.isLocked(i),
35605                 has_editor : cm.isCellEditable(i)
35606             };
35607         }
35608
35609         startRow = startRow || 0;
35610         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
35611
35612         // records to render
35613         var rs = ds.getRange(startRow, endRow);
35614
35615         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
35616     },
35617
35618     // As much as I hate to duplicate code, this was branched because FireFox really hates
35619     // [].join("") on strings. The performance difference was substantial enough to
35620     // branch this function
35621     doRender : Roo.isGecko ?
35622             function(cs, rs, ds, startRow, colCount, stripe){
35623                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35624                 // buffers
35625                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35626                 
35627                 var hasListener = this.grid.hasListener('rowclass');
35628                 var rowcfg = {};
35629                 for(var j = 0, len = rs.length; j < len; j++){
35630                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
35631                     for(var i = 0; i < colCount; i++){
35632                         c = cs[i];
35633                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35634                         p.id = c.id;
35635                         p.css = p.attr = "";
35636                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35637                         if(p.value == undefined || p.value === "") {
35638                             p.value = "&#160;";
35639                         }
35640                         if(c.has_editor){
35641                             p.css += ' x-grid-editable-cell';
35642                         }
35643                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
35644                             p.css +=  ' x-grid-dirty-cell';
35645                         }
35646                         var markup = ct.apply(p);
35647                         if(!c.locked){
35648                             cb+= markup;
35649                         }else{
35650                             lcb+= markup;
35651                         }
35652                     }
35653                     var alt = [];
35654                     if(stripe && ((rowIndex+1) % 2 == 0)){
35655                         alt.push("x-grid-row-alt")
35656                     }
35657                     if(r.dirty){
35658                         alt.push(  " x-grid-dirty-row");
35659                     }
35660                     rp.cells = lcb;
35661                     if(this.getRowClass){
35662                         alt.push(this.getRowClass(r, rowIndex));
35663                     }
35664                     if (hasListener) {
35665                         rowcfg = {
35666                              
35667                             record: r,
35668                             rowIndex : rowIndex,
35669                             rowClass : ''
35670                         };
35671                         this.grid.fireEvent('rowclass', this, rowcfg);
35672                         alt.push(rowcfg.rowClass);
35673                     }
35674                     rp.alt = alt.join(" ");
35675                     lbuf+= rt.apply(rp);
35676                     rp.cells = cb;
35677                     buf+=  rt.apply(rp);
35678                 }
35679                 return [lbuf, buf];
35680             } :
35681             function(cs, rs, ds, startRow, colCount, stripe){
35682                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35683                 // buffers
35684                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35685                 var hasListener = this.grid.hasListener('rowclass');
35686  
35687                 var rowcfg = {};
35688                 for(var j = 0, len = rs.length; j < len; j++){
35689                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
35690                     for(var i = 0; i < colCount; i++){
35691                         c = cs[i];
35692                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35693                         p.id = c.id;
35694                         p.css = p.attr = "";
35695                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35696                         if(p.value == undefined || p.value === "") {
35697                             p.value = "&#160;";
35698                         }
35699                         //Roo.log(c);
35700                          if(c.has_editor){
35701                             p.css += ' x-grid-editable-cell';
35702                         }
35703                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35704                             p.css += ' x-grid-dirty-cell' 
35705                         }
35706                         
35707                         var markup = ct.apply(p);
35708                         if(!c.locked){
35709                             cb[cb.length] = markup;
35710                         }else{
35711                             lcb[lcb.length] = markup;
35712                         }
35713                     }
35714                     var alt = [];
35715                     if(stripe && ((rowIndex+1) % 2 == 0)){
35716                         alt.push( "x-grid-row-alt");
35717                     }
35718                     if(r.dirty){
35719                         alt.push(" x-grid-dirty-row");
35720                     }
35721                     rp.cells = lcb;
35722                     if(this.getRowClass){
35723                         alt.push( this.getRowClass(r, rowIndex));
35724                     }
35725                     if (hasListener) {
35726                         rowcfg = {
35727                              
35728                             record: r,
35729                             rowIndex : rowIndex,
35730                             rowClass : ''
35731                         };
35732                         this.grid.fireEvent('rowclass', this, rowcfg);
35733                         alt.push(rowcfg.rowClass);
35734                     }
35735                     
35736                     rp.alt = alt.join(" ");
35737                     rp.cells = lcb.join("");
35738                     lbuf[lbuf.length] = rt.apply(rp);
35739                     rp.cells = cb.join("");
35740                     buf[buf.length] =  rt.apply(rp);
35741                 }
35742                 return [lbuf.join(""), buf.join("")];
35743             },
35744
35745     renderBody : function(){
35746         var markup = this.renderRows();
35747         var bt = this.templates.body;
35748         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
35749     },
35750
35751     /**
35752      * Refreshes the grid
35753      * @param {Boolean} headersToo
35754      */
35755     refresh : function(headersToo){
35756         this.fireEvent("beforerefresh", this);
35757         this.grid.stopEditing();
35758         var result = this.renderBody();
35759         this.lockedBody.update(result[0]);
35760         this.mainBody.update(result[1]);
35761         if(headersToo === true){
35762             this.updateHeaders();
35763             this.updateColumns();
35764             this.updateSplitters();
35765             this.updateHeaderSortState();
35766         }
35767         this.syncRowHeights();
35768         this.layout();
35769         this.fireEvent("refresh", this);
35770     },
35771
35772     handleColumnMove : function(cm, oldIndex, newIndex){
35773         this.indexMap = null;
35774         var s = this.getScrollState();
35775         this.refresh(true);
35776         this.restoreScroll(s);
35777         this.afterMove(newIndex);
35778     },
35779
35780     afterMove : function(colIndex){
35781         if(this.enableMoveAnim && Roo.enableFx){
35782             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
35783         }
35784         // if multisort - fix sortOrder, and reload..
35785         if (this.grid.dataSource.multiSort) {
35786             // the we can call sort again..
35787             var dm = this.grid.dataSource;
35788             var cm = this.grid.colModel;
35789             var so = [];
35790             for(var i = 0; i < cm.config.length; i++ ) {
35791                 
35792                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
35793                     continue; // dont' bother, it's not in sort list or being set.
35794                 }
35795                 
35796                 so.push(cm.config[i].dataIndex);
35797             };
35798             dm.sortOrder = so;
35799             dm.load(dm.lastOptions);
35800             
35801             
35802         }
35803         
35804     },
35805
35806     updateCell : function(dm, rowIndex, dataIndex){
35807         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
35808         if(typeof colIndex == "undefined"){ // not present in grid
35809             return;
35810         }
35811         var cm = this.grid.colModel;
35812         var cell = this.getCell(rowIndex, colIndex);
35813         var cellText = this.getCellText(rowIndex, colIndex);
35814
35815         var p = {
35816             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
35817             id : cm.getColumnId(colIndex),
35818             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
35819         };
35820         var renderer = cm.getRenderer(colIndex);
35821         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
35822         if(typeof val == "undefined" || val === "") {
35823             val = "&#160;";
35824         }
35825         cellText.innerHTML = val;
35826         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
35827         this.syncRowHeights(rowIndex, rowIndex);
35828     },
35829
35830     calcColumnWidth : function(colIndex, maxRowsToMeasure){
35831         var maxWidth = 0;
35832         if(this.grid.autoSizeHeaders){
35833             var h = this.getHeaderCellMeasure(colIndex);
35834             maxWidth = Math.max(maxWidth, h.scrollWidth);
35835         }
35836         var tb, index;
35837         if(this.cm.isLocked(colIndex)){
35838             tb = this.getLockedTable();
35839             index = colIndex;
35840         }else{
35841             tb = this.getBodyTable();
35842             index = colIndex - this.cm.getLockedCount();
35843         }
35844         if(tb && tb.rows){
35845             var rows = tb.rows;
35846             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
35847             for(var i = 0; i < stopIndex; i++){
35848                 var cell = rows[i].childNodes[index].firstChild;
35849                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
35850             }
35851         }
35852         return maxWidth + /*margin for error in IE*/ 5;
35853     },
35854     /**
35855      * Autofit a column to its content.
35856      * @param {Number} colIndex
35857      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
35858      */
35859      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
35860          if(this.cm.isHidden(colIndex)){
35861              return; // can't calc a hidden column
35862          }
35863         if(forceMinSize){
35864             var cid = this.cm.getColumnId(colIndex);
35865             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
35866            if(this.grid.autoSizeHeaders){
35867                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
35868            }
35869         }
35870         var newWidth = this.calcColumnWidth(colIndex);
35871         this.cm.setColumnWidth(colIndex,
35872             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
35873         if(!suppressEvent){
35874             this.grid.fireEvent("columnresize", colIndex, newWidth);
35875         }
35876     },
35877
35878     /**
35879      * Autofits all columns to their content and then expands to fit any extra space in the grid
35880      */
35881      autoSizeColumns : function(){
35882         var cm = this.grid.colModel;
35883         var colCount = cm.getColumnCount();
35884         for(var i = 0; i < colCount; i++){
35885             this.autoSizeColumn(i, true, true);
35886         }
35887         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
35888             this.fitColumns();
35889         }else{
35890             this.updateColumns();
35891             this.layout();
35892         }
35893     },
35894
35895     /**
35896      * Autofits all columns to the grid's width proportionate with their current size
35897      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
35898      */
35899     fitColumns : function(reserveScrollSpace){
35900         var cm = this.grid.colModel;
35901         var colCount = cm.getColumnCount();
35902         var cols = [];
35903         var width = 0;
35904         var i, w;
35905         for (i = 0; i < colCount; i++){
35906             if(!cm.isHidden(i) && !cm.isFixed(i)){
35907                 w = cm.getColumnWidth(i);
35908                 cols.push(i);
35909                 cols.push(w);
35910                 width += w;
35911             }
35912         }
35913         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
35914         if(reserveScrollSpace){
35915             avail -= 17;
35916         }
35917         var frac = (avail - cm.getTotalWidth())/width;
35918         while (cols.length){
35919             w = cols.pop();
35920             i = cols.pop();
35921             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
35922         }
35923         this.updateColumns();
35924         this.layout();
35925     },
35926
35927     onRowSelect : function(rowIndex){
35928         var row = this.getRowComposite(rowIndex);
35929         row.addClass("x-grid-row-selected");
35930     },
35931
35932     onRowDeselect : function(rowIndex){
35933         var row = this.getRowComposite(rowIndex);
35934         row.removeClass("x-grid-row-selected");
35935     },
35936
35937     onCellSelect : function(row, col){
35938         var cell = this.getCell(row, col);
35939         if(cell){
35940             Roo.fly(cell).addClass("x-grid-cell-selected");
35941         }
35942     },
35943
35944     onCellDeselect : function(row, col){
35945         var cell = this.getCell(row, col);
35946         if(cell){
35947             Roo.fly(cell).removeClass("x-grid-cell-selected");
35948         }
35949     },
35950
35951     updateHeaderSortState : function(){
35952         
35953         // sort state can be single { field: xxx, direction : yyy}
35954         // or   { xxx=>ASC , yyy : DESC ..... }
35955         
35956         var mstate = {};
35957         if (!this.ds.multiSort) { 
35958             var state = this.ds.getSortState();
35959             if(!state){
35960                 return;
35961             }
35962             mstate[state.field] = state.direction;
35963             // FIXME... - this is not used here.. but might be elsewhere..
35964             this.sortState = state;
35965             
35966         } else {
35967             mstate = this.ds.sortToggle;
35968         }
35969         //remove existing sort classes..
35970         
35971         var sc = this.sortClasses;
35972         var hds = this.el.select(this.headerSelector).removeClass(sc);
35973         
35974         for(var f in mstate) {
35975         
35976             var sortColumn = this.cm.findColumnIndex(f);
35977             
35978             if(sortColumn != -1){
35979                 var sortDir = mstate[f];        
35980                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
35981             }
35982         }
35983         
35984          
35985         
35986     },
35987
35988
35989     handleHeaderClick : function(g, index,e){
35990         
35991         Roo.log("header click");
35992         
35993         if (Roo.isTouch) {
35994             // touch events on header are handled by context
35995             this.handleHdCtx(g,index,e);
35996             return;
35997         }
35998         
35999         
36000         if(this.headersDisabled){
36001             return;
36002         }
36003         var dm = g.dataSource, cm = g.colModel;
36004         if(!cm.isSortable(index)){
36005             return;
36006         }
36007         g.stopEditing();
36008         
36009         if (dm.multiSort) {
36010             // update the sortOrder
36011             var so = [];
36012             for(var i = 0; i < cm.config.length; i++ ) {
36013                 
36014                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
36015                     continue; // dont' bother, it's not in sort list or being set.
36016                 }
36017                 
36018                 so.push(cm.config[i].dataIndex);
36019             };
36020             dm.sortOrder = so;
36021         }
36022         
36023         
36024         dm.sort(cm.getDataIndex(index));
36025     },
36026
36027
36028     destroy : function(){
36029         if(this.colMenu){
36030             this.colMenu.removeAll();
36031             Roo.menu.MenuMgr.unregister(this.colMenu);
36032             this.colMenu.getEl().remove();
36033             delete this.colMenu;
36034         }
36035         if(this.hmenu){
36036             this.hmenu.removeAll();
36037             Roo.menu.MenuMgr.unregister(this.hmenu);
36038             this.hmenu.getEl().remove();
36039             delete this.hmenu;
36040         }
36041         if(this.grid.enableColumnMove){
36042             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36043             if(dds){
36044                 for(var dd in dds){
36045                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
36046                         var elid = dds[dd].dragElId;
36047                         dds[dd].unreg();
36048                         Roo.get(elid).remove();
36049                     } else if(dds[dd].config.isTarget){
36050                         dds[dd].proxyTop.remove();
36051                         dds[dd].proxyBottom.remove();
36052                         dds[dd].unreg();
36053                     }
36054                     if(Roo.dd.DDM.locationCache[dd]){
36055                         delete Roo.dd.DDM.locationCache[dd];
36056                     }
36057                 }
36058                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36059             }
36060         }
36061         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
36062         this.bind(null, null);
36063         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
36064     },
36065
36066     handleLockChange : function(){
36067         this.refresh(true);
36068     },
36069
36070     onDenyColumnLock : function(){
36071
36072     },
36073
36074     onDenyColumnHide : function(){
36075
36076     },
36077
36078     handleHdMenuClick : function(item){
36079         var index = this.hdCtxIndex;
36080         var cm = this.cm, ds = this.ds;
36081         switch(item.id){
36082             case "asc":
36083                 ds.sort(cm.getDataIndex(index), "ASC");
36084                 break;
36085             case "desc":
36086                 ds.sort(cm.getDataIndex(index), "DESC");
36087                 break;
36088             case "lock":
36089                 var lc = cm.getLockedCount();
36090                 if(cm.getColumnCount(true) <= lc+1){
36091                     this.onDenyColumnLock();
36092                     return;
36093                 }
36094                 if(lc != index){
36095                     cm.setLocked(index, true, true);
36096                     cm.moveColumn(index, lc);
36097                     this.grid.fireEvent("columnmove", index, lc);
36098                 }else{
36099                     cm.setLocked(index, true);
36100                 }
36101             break;
36102             case "unlock":
36103                 var lc = cm.getLockedCount();
36104                 if((lc-1) != index){
36105                     cm.setLocked(index, false, true);
36106                     cm.moveColumn(index, lc-1);
36107                     this.grid.fireEvent("columnmove", index, lc-1);
36108                 }else{
36109                     cm.setLocked(index, false);
36110                 }
36111             break;
36112             case 'wider': // used to expand cols on touch..
36113             case 'narrow':
36114                 var cw = cm.getColumnWidth(index);
36115                 cw += (item.id == 'wider' ? 1 : -1) * 50;
36116                 cw = Math.max(0, cw);
36117                 cw = Math.min(cw,4000);
36118                 cm.setColumnWidth(index, cw);
36119                 break;
36120                 
36121             default:
36122                 index = cm.getIndexById(item.id.substr(4));
36123                 if(index != -1){
36124                     if(item.checked && cm.getColumnCount(true) <= 1){
36125                         this.onDenyColumnHide();
36126                         return false;
36127                     }
36128                     cm.setHidden(index, item.checked);
36129                 }
36130         }
36131         return true;
36132     },
36133
36134     beforeColMenuShow : function(){
36135         var cm = this.cm,  colCount = cm.getColumnCount();
36136         this.colMenu.removeAll();
36137         for(var i = 0; i < colCount; i++){
36138             this.colMenu.add(new Roo.menu.CheckItem({
36139                 id: "col-"+cm.getColumnId(i),
36140                 text: cm.getColumnHeader(i),
36141                 checked: !cm.isHidden(i),
36142                 hideOnClick:false
36143             }));
36144         }
36145     },
36146
36147     handleHdCtx : function(g, index, e){
36148         e.stopEvent();
36149         var hd = this.getHeaderCell(index);
36150         this.hdCtxIndex = index;
36151         var ms = this.hmenu.items, cm = this.cm;
36152         ms.get("asc").setDisabled(!cm.isSortable(index));
36153         ms.get("desc").setDisabled(!cm.isSortable(index));
36154         if(this.grid.enableColLock !== false){
36155             ms.get("lock").setDisabled(cm.isLocked(index));
36156             ms.get("unlock").setDisabled(!cm.isLocked(index));
36157         }
36158         this.hmenu.show(hd, "tl-bl");
36159     },
36160
36161     handleHdOver : function(e){
36162         var hd = this.findHeaderCell(e.getTarget());
36163         if(hd && !this.headersDisabled){
36164             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
36165                this.fly(hd).addClass("x-grid-hd-over");
36166             }
36167         }
36168     },
36169
36170     handleHdOut : function(e){
36171         var hd = this.findHeaderCell(e.getTarget());
36172         if(hd){
36173             this.fly(hd).removeClass("x-grid-hd-over");
36174         }
36175     },
36176
36177     handleSplitDblClick : function(e, t){
36178         var i = this.getCellIndex(t);
36179         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
36180             this.autoSizeColumn(i, true);
36181             this.layout();
36182         }
36183     },
36184
36185     render : function(){
36186
36187         var cm = this.cm;
36188         var colCount = cm.getColumnCount();
36189
36190         if(this.grid.monitorWindowResize === true){
36191             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36192         }
36193         var header = this.renderHeaders();
36194         var body = this.templates.body.apply({rows:""});
36195         var html = this.templates.master.apply({
36196             lockedBody: body,
36197             body: body,
36198             lockedHeader: header[0],
36199             header: header[1]
36200         });
36201
36202         //this.updateColumns();
36203
36204         this.grid.getGridEl().dom.innerHTML = html;
36205
36206         this.initElements();
36207         
36208         // a kludge to fix the random scolling effect in webkit
36209         this.el.on("scroll", function() {
36210             this.el.dom.scrollTop=0; // hopefully not recursive..
36211         },this);
36212
36213         this.scroller.on("scroll", this.handleScroll, this);
36214         this.lockedBody.on("mousewheel", this.handleWheel, this);
36215         this.mainBody.on("mousewheel", this.handleWheel, this);
36216
36217         this.mainHd.on("mouseover", this.handleHdOver, this);
36218         this.mainHd.on("mouseout", this.handleHdOut, this);
36219         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
36220                 {delegate: "."+this.splitClass});
36221
36222         this.lockedHd.on("mouseover", this.handleHdOver, this);
36223         this.lockedHd.on("mouseout", this.handleHdOut, this);
36224         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
36225                 {delegate: "."+this.splitClass});
36226
36227         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
36228             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36229         }
36230
36231         this.updateSplitters();
36232
36233         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
36234             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36235             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36236         }
36237
36238         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
36239             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
36240             this.hmenu.add(
36241                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
36242                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
36243             );
36244             if(this.grid.enableColLock !== false){
36245                 this.hmenu.add('-',
36246                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
36247                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
36248                 );
36249             }
36250             if (Roo.isTouch) {
36251                  this.hmenu.add('-',
36252                     {id:"wider", text: this.columnsWiderText},
36253                     {id:"narrow", text: this.columnsNarrowText }
36254                 );
36255                 
36256                  
36257             }
36258             
36259             if(this.grid.enableColumnHide !== false){
36260
36261                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
36262                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
36263                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
36264
36265                 this.hmenu.add('-',
36266                     {id:"columns", text: this.columnsText, menu: this.colMenu}
36267                 );
36268             }
36269             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
36270
36271             this.grid.on("headercontextmenu", this.handleHdCtx, this);
36272         }
36273
36274         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
36275             this.dd = new Roo.grid.GridDragZone(this.grid, {
36276                 ddGroup : this.grid.ddGroup || 'GridDD'
36277             });
36278             
36279         }
36280
36281         /*
36282         for(var i = 0; i < colCount; i++){
36283             if(cm.isHidden(i)){
36284                 this.hideColumn(i);
36285             }
36286             if(cm.config[i].align){
36287                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
36288                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
36289             }
36290         }*/
36291         
36292         this.updateHeaderSortState();
36293
36294         this.beforeInitialResize();
36295         this.layout(true);
36296
36297         // two part rendering gives faster view to the user
36298         this.renderPhase2.defer(1, this);
36299     },
36300
36301     renderPhase2 : function(){
36302         // render the rows now
36303         this.refresh();
36304         if(this.grid.autoSizeColumns){
36305             this.autoSizeColumns();
36306         }
36307     },
36308
36309     beforeInitialResize : function(){
36310
36311     },
36312
36313     onColumnSplitterMoved : function(i, w){
36314         this.userResized = true;
36315         var cm = this.grid.colModel;
36316         cm.setColumnWidth(i, w, true);
36317         var cid = cm.getColumnId(i);
36318         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36319         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36320         this.updateSplitters();
36321         this.layout();
36322         this.grid.fireEvent("columnresize", i, w);
36323     },
36324
36325     syncRowHeights : function(startIndex, endIndex){
36326         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
36327             startIndex = startIndex || 0;
36328             var mrows = this.getBodyTable().rows;
36329             var lrows = this.getLockedTable().rows;
36330             var len = mrows.length-1;
36331             endIndex = Math.min(endIndex || len, len);
36332             for(var i = startIndex; i <= endIndex; i++){
36333                 var m = mrows[i], l = lrows[i];
36334                 var h = Math.max(m.offsetHeight, l.offsetHeight);
36335                 m.style.height = l.style.height = h + "px";
36336             }
36337         }
36338     },
36339
36340     layout : function(initialRender, is2ndPass)
36341     {
36342         var g = this.grid;
36343         var auto = g.autoHeight;
36344         var scrollOffset = 16;
36345         var c = g.getGridEl(), cm = this.cm,
36346                 expandCol = g.autoExpandColumn,
36347                 gv = this;
36348         //c.beginMeasure();
36349
36350         if(!c.dom.offsetWidth){ // display:none?
36351             if(initialRender){
36352                 this.lockedWrap.show();
36353                 this.mainWrap.show();
36354             }
36355             return;
36356         }
36357
36358         var hasLock = this.cm.isLocked(0);
36359
36360         var tbh = this.headerPanel.getHeight();
36361         var bbh = this.footerPanel.getHeight();
36362
36363         if(auto){
36364             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
36365             var newHeight = ch + c.getBorderWidth("tb");
36366             if(g.maxHeight){
36367                 newHeight = Math.min(g.maxHeight, newHeight);
36368             }
36369             c.setHeight(newHeight);
36370         }
36371
36372         if(g.autoWidth){
36373             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
36374         }
36375
36376         var s = this.scroller;
36377
36378         var csize = c.getSize(true);
36379
36380         this.el.setSize(csize.width, csize.height);
36381
36382         this.headerPanel.setWidth(csize.width);
36383         this.footerPanel.setWidth(csize.width);
36384
36385         var hdHeight = this.mainHd.getHeight();
36386         var vw = csize.width;
36387         var vh = csize.height - (tbh + bbh);
36388
36389         s.setSize(vw, vh);
36390
36391         var bt = this.getBodyTable();
36392         
36393         if(cm.getLockedCount() == cm.config.length){
36394             bt = this.getLockedTable();
36395         }
36396         
36397         var ltWidth = hasLock ?
36398                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
36399
36400         var scrollHeight = bt.offsetHeight;
36401         var scrollWidth = ltWidth + bt.offsetWidth;
36402         var vscroll = false, hscroll = false;
36403
36404         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
36405
36406         var lw = this.lockedWrap, mw = this.mainWrap;
36407         var lb = this.lockedBody, mb = this.mainBody;
36408
36409         setTimeout(function(){
36410             var t = s.dom.offsetTop;
36411             var w = s.dom.clientWidth,
36412                 h = s.dom.clientHeight;
36413
36414             lw.setTop(t);
36415             lw.setSize(ltWidth, h);
36416
36417             mw.setLeftTop(ltWidth, t);
36418             mw.setSize(w-ltWidth, h);
36419
36420             lb.setHeight(h-hdHeight);
36421             mb.setHeight(h-hdHeight);
36422
36423             if(is2ndPass !== true && !gv.userResized && expandCol){
36424                 // high speed resize without full column calculation
36425                 
36426                 var ci = cm.getIndexById(expandCol);
36427                 if (ci < 0) {
36428                     ci = cm.findColumnIndex(expandCol);
36429                 }
36430                 ci = Math.max(0, ci); // make sure it's got at least the first col.
36431                 var expandId = cm.getColumnId(ci);
36432                 var  tw = cm.getTotalWidth(false);
36433                 var currentWidth = cm.getColumnWidth(ci);
36434                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
36435                 if(currentWidth != cw){
36436                     cm.setColumnWidth(ci, cw, true);
36437                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36438                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36439                     gv.updateSplitters();
36440                     gv.layout(false, true);
36441                 }
36442             }
36443
36444             if(initialRender){
36445                 lw.show();
36446                 mw.show();
36447             }
36448             //c.endMeasure();
36449         }, 10);
36450     },
36451
36452     onWindowResize : function(){
36453         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
36454             return;
36455         }
36456         this.layout();
36457     },
36458
36459     appendFooter : function(parentEl){
36460         return null;
36461     },
36462
36463     sortAscText : "Sort Ascending",
36464     sortDescText : "Sort Descending",
36465     lockText : "Lock Column",
36466     unlockText : "Unlock Column",
36467     columnsText : "Columns",
36468  
36469     columnsWiderText : "Wider",
36470     columnsNarrowText : "Thinner"
36471 });
36472
36473
36474 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
36475     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
36476     this.proxy.el.addClass('x-grid3-col-dd');
36477 };
36478
36479 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
36480     handleMouseDown : function(e){
36481
36482     },
36483
36484     callHandleMouseDown : function(e){
36485         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
36486     }
36487 });
36488 /*
36489  * Based on:
36490  * Ext JS Library 1.1.1
36491  * Copyright(c) 2006-2007, Ext JS, LLC.
36492  *
36493  * Originally Released Under LGPL - original licence link has changed is not relivant.
36494  *
36495  * Fork - LGPL
36496  * <script type="text/javascript">
36497  */
36498  /**
36499  * @extends Roo.dd.DDProxy
36500  * @class Roo.grid.SplitDragZone
36501  * Support for Column Header resizing
36502  * @constructor
36503  * @param {Object} config
36504  */
36505 // private
36506 // This is a support class used internally by the Grid components
36507 Roo.grid.SplitDragZone = function(grid, hd, hd2){
36508     this.grid = grid;
36509     this.view = grid.getView();
36510     this.proxy = this.view.resizeProxy;
36511     Roo.grid.SplitDragZone.superclass.constructor.call(
36512         this,
36513         hd, // ID
36514         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
36515         {  // CONFIG
36516             dragElId : Roo.id(this.proxy.dom),
36517             resizeFrame:false
36518         }
36519     );
36520     
36521     this.setHandleElId(Roo.id(hd));
36522     if (hd2 !== false) {
36523         this.setOuterHandleElId(Roo.id(hd2));
36524     }
36525     
36526     this.scroll = false;
36527 };
36528 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
36529     fly: Roo.Element.fly,
36530
36531     b4StartDrag : function(x, y){
36532         this.view.headersDisabled = true;
36533         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
36534                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
36535         );
36536         this.proxy.setHeight(h);
36537         
36538         // for old system colWidth really stored the actual width?
36539         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
36540         // which in reality did not work.. - it worked only for fixed sizes
36541         // for resizable we need to use actual sizes.
36542         var w = this.cm.getColumnWidth(this.cellIndex);
36543         if (!this.view.mainWrap) {
36544             // bootstrap.
36545             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
36546         }
36547         
36548         
36549         
36550         // this was w-this.grid.minColumnWidth;
36551         // doesnt really make sense? - w = thie curren width or the rendered one?
36552         var minw = Math.max(w-this.grid.minColumnWidth, 0);
36553         this.resetConstraints();
36554         this.setXConstraint(minw, 1000);
36555         this.setYConstraint(0, 0);
36556         this.minX = x - minw;
36557         this.maxX = x + 1000;
36558         this.startPos = x;
36559         if (!this.view.mainWrap) { // this is Bootstrap code..
36560             this.getDragEl().style.display='block';
36561         }
36562         
36563         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
36564     },
36565
36566
36567     handleMouseDown : function(e){
36568         ev = Roo.EventObject.setEvent(e);
36569         var t = this.fly(ev.getTarget());
36570         if(t.hasClass("x-grid-split")){
36571             this.cellIndex = this.view.getCellIndex(t.dom);
36572             this.split = t.dom;
36573             this.cm = this.grid.colModel;
36574             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
36575                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
36576             }
36577         }
36578     },
36579
36580     endDrag : function(e){
36581         this.view.headersDisabled = false;
36582         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
36583         var diff = endX - this.startPos;
36584         // 
36585         var w = this.cm.getColumnWidth(this.cellIndex);
36586         if (!this.view.mainWrap) {
36587             w = 0;
36588         }
36589         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
36590     },
36591
36592     autoOffset : function(){
36593         this.setDelta(0,0);
36594     }
36595 });/*
36596  * Based on:
36597  * Ext JS Library 1.1.1
36598  * Copyright(c) 2006-2007, Ext JS, LLC.
36599  *
36600  * Originally Released Under LGPL - original licence link has changed is not relivant.
36601  *
36602  * Fork - LGPL
36603  * <script type="text/javascript">
36604  */
36605  
36606 // private
36607 // This is a support class used internally by the Grid components
36608 Roo.grid.GridDragZone = function(grid, config){
36609     this.view = grid.getView();
36610     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
36611     if(this.view.lockedBody){
36612         this.setHandleElId(Roo.id(this.view.mainBody.dom));
36613         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
36614     }
36615     this.scroll = false;
36616     this.grid = grid;
36617     this.ddel = document.createElement('div');
36618     this.ddel.className = 'x-grid-dd-wrap';
36619 };
36620
36621 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
36622     ddGroup : "GridDD",
36623
36624     getDragData : function(e){
36625         var t = Roo.lib.Event.getTarget(e);
36626         var rowIndex = this.view.findRowIndex(t);
36627         var sm = this.grid.selModel;
36628             
36629         //Roo.log(rowIndex);
36630         
36631         if (sm.getSelectedCell) {
36632             // cell selection..
36633             if (!sm.getSelectedCell()) {
36634                 return false;
36635             }
36636             if (rowIndex != sm.getSelectedCell()[0]) {
36637                 return false;
36638             }
36639         
36640         }
36641         if (sm.getSelections && sm.getSelections().length < 1) {
36642             return false;
36643         }
36644         
36645         
36646         // before it used to all dragging of unseleted... - now we dont do that.
36647         if(rowIndex !== false){
36648             
36649             // if editorgrid.. 
36650             
36651             
36652             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
36653                
36654             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
36655               //  
36656             //}
36657             if (e.hasModifier()){
36658                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
36659             }
36660             
36661             Roo.log("getDragData");
36662             
36663             return {
36664                 grid: this.grid,
36665                 ddel: this.ddel,
36666                 rowIndex: rowIndex,
36667                 selections: sm.getSelections ? sm.getSelections() : (
36668                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
36669             };
36670         }
36671         return false;
36672     },
36673     
36674     
36675     onInitDrag : function(e){
36676         var data = this.dragData;
36677         this.ddel.innerHTML = this.grid.getDragDropText();
36678         this.proxy.update(this.ddel);
36679         // fire start drag?
36680     },
36681
36682     afterRepair : function(){
36683         this.dragging = false;
36684     },
36685
36686     getRepairXY : function(e, data){
36687         return false;
36688     },
36689
36690     onEndDrag : function(data, e){
36691         // fire end drag?
36692     },
36693
36694     onValidDrop : function(dd, e, id){
36695         // fire drag drop?
36696         this.hideProxy();
36697     },
36698
36699     beforeInvalidDrop : function(e, id){
36700
36701     }
36702 });/*
36703  * Based on:
36704  * Ext JS Library 1.1.1
36705  * Copyright(c) 2006-2007, Ext JS, LLC.
36706  *
36707  * Originally Released Under LGPL - original licence link has changed is not relivant.
36708  *
36709  * Fork - LGPL
36710  * <script type="text/javascript">
36711  */
36712  
36713
36714 /**
36715  * @class Roo.grid.ColumnModel
36716  * @extends Roo.util.Observable
36717  * This is the default implementation of a ColumnModel used by the Grid. It defines
36718  * the columns in the grid.
36719  * <br>Usage:<br>
36720  <pre><code>
36721  var colModel = new Roo.grid.ColumnModel([
36722         {header: "Ticker", width: 60, sortable: true, locked: true},
36723         {header: "Company Name", width: 150, sortable: true},
36724         {header: "Market Cap.", width: 100, sortable: true},
36725         {header: "$ Sales", width: 100, sortable: true, renderer: money},
36726         {header: "Employees", width: 100, sortable: true, resizable: false}
36727  ]);
36728  </code></pre>
36729  * <p>
36730  
36731  * The config options listed for this class are options which may appear in each
36732  * individual column definition.
36733  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
36734  * @constructor
36735  * @param {Object} config An Array of column config objects. See this class's
36736  * config objects for details.
36737 */
36738 Roo.grid.ColumnModel = function(config){
36739         /**
36740      * The config passed into the constructor
36741      */
36742     this.config = []; //config;
36743     this.lookup = {};
36744
36745     // if no id, create one
36746     // if the column does not have a dataIndex mapping,
36747     // map it to the order it is in the config
36748     for(var i = 0, len = config.length; i < len; i++){
36749         this.addColumn(config[i]);
36750         
36751     }
36752
36753     /**
36754      * The width of columns which have no width specified (defaults to 100)
36755      * @type Number
36756      */
36757     this.defaultWidth = 100;
36758
36759     /**
36760      * Default sortable of columns which have no sortable specified (defaults to false)
36761      * @type Boolean
36762      */
36763     this.defaultSortable = false;
36764
36765     this.addEvents({
36766         /**
36767              * @event widthchange
36768              * Fires when the width of a column changes.
36769              * @param {ColumnModel} this
36770              * @param {Number} columnIndex The column index
36771              * @param {Number} newWidth The new width
36772              */
36773             "widthchange": true,
36774         /**
36775              * @event headerchange
36776              * Fires when the text of a header changes.
36777              * @param {ColumnModel} this
36778              * @param {Number} columnIndex The column index
36779              * @param {Number} newText The new header text
36780              */
36781             "headerchange": true,
36782         /**
36783              * @event hiddenchange
36784              * Fires when a column is hidden or "unhidden".
36785              * @param {ColumnModel} this
36786              * @param {Number} columnIndex The column index
36787              * @param {Boolean} hidden true if hidden, false otherwise
36788              */
36789             "hiddenchange": true,
36790             /**
36791          * @event columnmoved
36792          * Fires when a column is moved.
36793          * @param {ColumnModel} this
36794          * @param {Number} oldIndex
36795          * @param {Number} newIndex
36796          */
36797         "columnmoved" : true,
36798         /**
36799          * @event columlockchange
36800          * Fires when a column's locked state is changed
36801          * @param {ColumnModel} this
36802          * @param {Number} colIndex
36803          * @param {Boolean} locked true if locked
36804          */
36805         "columnlockchange" : true
36806     });
36807     Roo.grid.ColumnModel.superclass.constructor.call(this);
36808 };
36809 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
36810     /**
36811      * @cfg {String} header The header text to display in the Grid view.
36812      */
36813         /**
36814      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
36815      */
36816         /**
36817      * @cfg {String} smHeader Header at Bootsrap Small width
36818      */
36819         /**
36820      * @cfg {String} mdHeader Header at Bootsrap Medium width
36821      */
36822         /**
36823      * @cfg {String} lgHeader Header at Bootsrap Large width
36824      */
36825         /**
36826      * @cfg {String} xlHeader Header at Bootsrap extra Large width
36827      */
36828     /**
36829      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
36830      * {@link Roo.data.Record} definition from which to draw the column's value. If not
36831      * specified, the column's index is used as an index into the Record's data Array.
36832      */
36833     /**
36834      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
36835      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
36836      */
36837     /**
36838      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
36839      * Defaults to the value of the {@link #defaultSortable} property.
36840      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
36841      */
36842     /**
36843      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
36844      */
36845     /**
36846      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
36847      */
36848     /**
36849      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
36850      */
36851     /**
36852      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
36853      */
36854     /**
36855      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
36856      * given the cell's data value. See {@link #setRenderer}. If not specified, the
36857      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
36858      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
36859      */
36860        /**
36861      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
36862      */
36863     /**
36864      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
36865      */
36866     /**
36867      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
36868      */
36869     /**
36870      * @cfg {String} cursor (Optional)
36871      */
36872     /**
36873      * @cfg {String} tooltip (Optional)
36874      */
36875     /**
36876      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
36877      */
36878     /**
36879      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
36880      */
36881     /**
36882      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
36883      */
36884     /**
36885      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
36886      */
36887         /**
36888      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
36889      */
36890     /**
36891      * Returns the id of the column at the specified index.
36892      * @param {Number} index The column index
36893      * @return {String} the id
36894      */
36895     getColumnId : function(index){
36896         return this.config[index].id;
36897     },
36898
36899     /**
36900      * Returns the column for a specified id.
36901      * @param {String} id The column id
36902      * @return {Object} the column
36903      */
36904     getColumnById : function(id){
36905         return this.lookup[id];
36906     },
36907
36908     
36909     /**
36910      * Returns the column Object for a specified dataIndex.
36911      * @param {String} dataIndex The column dataIndex
36912      * @return {Object|Boolean} the column or false if not found
36913      */
36914     getColumnByDataIndex: function(dataIndex){
36915         var index = this.findColumnIndex(dataIndex);
36916         return index > -1 ? this.config[index] : false;
36917     },
36918     
36919     /**
36920      * Returns the index for a specified column id.
36921      * @param {String} id The column id
36922      * @return {Number} the index, or -1 if not found
36923      */
36924     getIndexById : function(id){
36925         for(var i = 0, len = this.config.length; i < len; i++){
36926             if(this.config[i].id == id){
36927                 return i;
36928             }
36929         }
36930         return -1;
36931     },
36932     
36933     /**
36934      * Returns the index for a specified column dataIndex.
36935      * @param {String} dataIndex The column dataIndex
36936      * @return {Number} the index, or -1 if not found
36937      */
36938     
36939     findColumnIndex : function(dataIndex){
36940         for(var i = 0, len = this.config.length; i < len; i++){
36941             if(this.config[i].dataIndex == dataIndex){
36942                 return i;
36943             }
36944         }
36945         return -1;
36946     },
36947     
36948     
36949     moveColumn : function(oldIndex, newIndex){
36950         var c = this.config[oldIndex];
36951         this.config.splice(oldIndex, 1);
36952         this.config.splice(newIndex, 0, c);
36953         this.dataMap = null;
36954         this.fireEvent("columnmoved", this, oldIndex, newIndex);
36955     },
36956
36957     isLocked : function(colIndex){
36958         return this.config[colIndex].locked === true;
36959     },
36960
36961     setLocked : function(colIndex, value, suppressEvent){
36962         if(this.isLocked(colIndex) == value){
36963             return;
36964         }
36965         this.config[colIndex].locked = value;
36966         if(!suppressEvent){
36967             this.fireEvent("columnlockchange", this, colIndex, value);
36968         }
36969     },
36970
36971     getTotalLockedWidth : function(){
36972         var totalWidth = 0;
36973         for(var i = 0; i < this.config.length; i++){
36974             if(this.isLocked(i) && !this.isHidden(i)){
36975                 this.totalWidth += this.getColumnWidth(i);
36976             }
36977         }
36978         return totalWidth;
36979     },
36980
36981     getLockedCount : function(){
36982         for(var i = 0, len = this.config.length; i < len; i++){
36983             if(!this.isLocked(i)){
36984                 return i;
36985             }
36986         }
36987         
36988         return this.config.length;
36989     },
36990
36991     /**
36992      * Returns the number of columns.
36993      * @return {Number}
36994      */
36995     getColumnCount : function(visibleOnly){
36996         if(visibleOnly === true){
36997             var c = 0;
36998             for(var i = 0, len = this.config.length; i < len; i++){
36999                 if(!this.isHidden(i)){
37000                     c++;
37001                 }
37002             }
37003             return c;
37004         }
37005         return this.config.length;
37006     },
37007
37008     /**
37009      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
37010      * @param {Function} fn
37011      * @param {Object} scope (optional)
37012      * @return {Array} result
37013      */
37014     getColumnsBy : function(fn, scope){
37015         var r = [];
37016         for(var i = 0, len = this.config.length; i < len; i++){
37017             var c = this.config[i];
37018             if(fn.call(scope||this, c, i) === true){
37019                 r[r.length] = c;
37020             }
37021         }
37022         return r;
37023     },
37024
37025     /**
37026      * Returns true if the specified column is sortable.
37027      * @param {Number} col The column index
37028      * @return {Boolean}
37029      */
37030     isSortable : function(col){
37031         if(typeof this.config[col].sortable == "undefined"){
37032             return this.defaultSortable;
37033         }
37034         return this.config[col].sortable;
37035     },
37036
37037     /**
37038      * Returns the rendering (formatting) function defined for the column.
37039      * @param {Number} col The column index.
37040      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
37041      */
37042     getRenderer : function(col){
37043         if(!this.config[col].renderer){
37044             return Roo.grid.ColumnModel.defaultRenderer;
37045         }
37046         return this.config[col].renderer;
37047     },
37048
37049     /**
37050      * Sets the rendering (formatting) function for a column.
37051      * @param {Number} col The column index
37052      * @param {Function} fn The function to use to process the cell's raw data
37053      * to return HTML markup for the grid view. The render function is called with
37054      * the following parameters:<ul>
37055      * <li>Data value.</li>
37056      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
37057      * <li>css A CSS style string to apply to the table cell.</li>
37058      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
37059      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
37060      * <li>Row index</li>
37061      * <li>Column index</li>
37062      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
37063      */
37064     setRenderer : function(col, fn){
37065         this.config[col].renderer = fn;
37066     },
37067
37068     /**
37069      * Returns the width for the specified column.
37070      * @param {Number} col The column index
37071      * @param (optional) {String} gridSize bootstrap width size.
37072      * @return {Number}
37073      */
37074     getColumnWidth : function(col, gridSize)
37075         {
37076                 var cfg = this.config[col];
37077                 
37078                 if (typeof(gridSize) == 'undefined') {
37079                         return cfg.width * 1 || this.defaultWidth;
37080                 }
37081                 if (gridSize === false) { // if we set it..
37082                         return cfg.width || false;
37083                 }
37084                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
37085                 
37086                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
37087                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
37088                                 continue;
37089                         }
37090                         return cfg[ sizes[i] ];
37091                 }
37092                 return 1;
37093                 
37094     },
37095
37096     /**
37097      * Sets the width for a column.
37098      * @param {Number} col The column index
37099      * @param {Number} width The new width
37100      */
37101     setColumnWidth : function(col, width, suppressEvent){
37102         this.config[col].width = width;
37103         this.totalWidth = null;
37104         if(!suppressEvent){
37105              this.fireEvent("widthchange", this, col, width);
37106         }
37107     },
37108
37109     /**
37110      * Returns the total width of all columns.
37111      * @param {Boolean} includeHidden True to include hidden column widths
37112      * @return {Number}
37113      */
37114     getTotalWidth : function(includeHidden){
37115         if(!this.totalWidth){
37116             this.totalWidth = 0;
37117             for(var i = 0, len = this.config.length; i < len; i++){
37118                 if(includeHidden || !this.isHidden(i)){
37119                     this.totalWidth += this.getColumnWidth(i);
37120                 }
37121             }
37122         }
37123         return this.totalWidth;
37124     },
37125
37126     /**
37127      * Returns the header for the specified column.
37128      * @param {Number} col The column index
37129      * @return {String}
37130      */
37131     getColumnHeader : function(col){
37132         return this.config[col].header;
37133     },
37134
37135     /**
37136      * Sets the header for a column.
37137      * @param {Number} col The column index
37138      * @param {String} header The new header
37139      */
37140     setColumnHeader : function(col, header){
37141         this.config[col].header = header;
37142         this.fireEvent("headerchange", this, col, header);
37143     },
37144
37145     /**
37146      * Returns the tooltip for the specified column.
37147      * @param {Number} col The column index
37148      * @return {String}
37149      */
37150     getColumnTooltip : function(col){
37151             return this.config[col].tooltip;
37152     },
37153     /**
37154      * Sets the tooltip for a column.
37155      * @param {Number} col The column index
37156      * @param {String} tooltip The new tooltip
37157      */
37158     setColumnTooltip : function(col, tooltip){
37159             this.config[col].tooltip = tooltip;
37160     },
37161
37162     /**
37163      * Returns the dataIndex for the specified column.
37164      * @param {Number} col The column index
37165      * @return {Number}
37166      */
37167     getDataIndex : function(col){
37168         return this.config[col].dataIndex;
37169     },
37170
37171     /**
37172      * Sets the dataIndex for a column.
37173      * @param {Number} col The column index
37174      * @param {Number} dataIndex The new dataIndex
37175      */
37176     setDataIndex : function(col, dataIndex){
37177         this.config[col].dataIndex = dataIndex;
37178     },
37179
37180     
37181     
37182     /**
37183      * Returns true if the cell is editable.
37184      * @param {Number} colIndex The column index
37185      * @param {Number} rowIndex The row index - this is nto actually used..?
37186      * @return {Boolean}
37187      */
37188     isCellEditable : function(colIndex, rowIndex){
37189         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
37190     },
37191
37192     /**
37193      * Returns the editor defined for the cell/column.
37194      * return false or null to disable editing.
37195      * @param {Number} colIndex The column index
37196      * @param {Number} rowIndex The row index
37197      * @return {Object}
37198      */
37199     getCellEditor : function(colIndex, rowIndex){
37200         return this.config[colIndex].editor;
37201     },
37202
37203     /**
37204      * Sets if a column is editable.
37205      * @param {Number} col The column index
37206      * @param {Boolean} editable True if the column is editable
37207      */
37208     setEditable : function(col, editable){
37209         this.config[col].editable = editable;
37210     },
37211
37212
37213     /**
37214      * Returns true if the column is hidden.
37215      * @param {Number} colIndex The column index
37216      * @return {Boolean}
37217      */
37218     isHidden : function(colIndex){
37219         return this.config[colIndex].hidden;
37220     },
37221
37222
37223     /**
37224      * Returns true if the column width cannot be changed
37225      */
37226     isFixed : function(colIndex){
37227         return this.config[colIndex].fixed;
37228     },
37229
37230     /**
37231      * Returns true if the column can be resized
37232      * @return {Boolean}
37233      */
37234     isResizable : function(colIndex){
37235         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
37236     },
37237     /**
37238      * Sets if a column is hidden.
37239      * @param {Number} colIndex The column index
37240      * @param {Boolean} hidden True if the column is hidden
37241      */
37242     setHidden : function(colIndex, hidden){
37243         this.config[colIndex].hidden = hidden;
37244         this.totalWidth = null;
37245         this.fireEvent("hiddenchange", this, colIndex, hidden);
37246     },
37247
37248     /**
37249      * Sets the editor for a column.
37250      * @param {Number} col The column index
37251      * @param {Object} editor The editor object
37252      */
37253     setEditor : function(col, editor){
37254         this.config[col].editor = editor;
37255     },
37256     /**
37257      * Add a column (experimental...) - defaults to adding to the end..
37258      * @param {Object} config 
37259     */
37260     addColumn : function(c)
37261     {
37262     
37263         var i = this.config.length;
37264         this.config[i] = c;
37265         
37266         if(typeof c.dataIndex == "undefined"){
37267             c.dataIndex = i;
37268         }
37269         if(typeof c.renderer == "string"){
37270             c.renderer = Roo.util.Format[c.renderer];
37271         }
37272         if(typeof c.id == "undefined"){
37273             c.id = Roo.id();
37274         }
37275         if(c.editor && c.editor.xtype){
37276             c.editor  = Roo.factory(c.editor, Roo.grid);
37277         }
37278         if(c.editor && c.editor.isFormField){
37279             c.editor = new Roo.grid.GridEditor(c.editor);
37280         }
37281         this.lookup[c.id] = c;
37282     }
37283     
37284 });
37285
37286 Roo.grid.ColumnModel.defaultRenderer = function(value)
37287 {
37288     if(typeof value == "object") {
37289         return value;
37290     }
37291         if(typeof value == "string" && value.length < 1){
37292             return "&#160;";
37293         }
37294     
37295         return String.format("{0}", value);
37296 };
37297
37298 // Alias for backwards compatibility
37299 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
37300 /*
37301  * Based on:
37302  * Ext JS Library 1.1.1
37303  * Copyright(c) 2006-2007, Ext JS, LLC.
37304  *
37305  * Originally Released Under LGPL - original licence link has changed is not relivant.
37306  *
37307  * Fork - LGPL
37308  * <script type="text/javascript">
37309  */
37310
37311 /**
37312  * @class Roo.grid.AbstractSelectionModel
37313  * @extends Roo.util.Observable
37314  * @abstract
37315  * Abstract base class for grid SelectionModels.  It provides the interface that should be
37316  * implemented by descendant classes.  This class should not be directly instantiated.
37317  * @constructor
37318  */
37319 Roo.grid.AbstractSelectionModel = function(){
37320     this.locked = false;
37321     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
37322 };
37323
37324 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
37325     /** @ignore Called by the grid automatically. Do not call directly. */
37326     init : function(grid){
37327         this.grid = grid;
37328         this.initEvents();
37329     },
37330
37331     /**
37332      * Locks the selections.
37333      */
37334     lock : function(){
37335         this.locked = true;
37336     },
37337
37338     /**
37339      * Unlocks the selections.
37340      */
37341     unlock : function(){
37342         this.locked = false;
37343     },
37344
37345     /**
37346      * Returns true if the selections are locked.
37347      * @return {Boolean}
37348      */
37349     isLocked : function(){
37350         return this.locked;
37351     }
37352 });/*
37353  * Based on:
37354  * Ext JS Library 1.1.1
37355  * Copyright(c) 2006-2007, Ext JS, LLC.
37356  *
37357  * Originally Released Under LGPL - original licence link has changed is not relivant.
37358  *
37359  * Fork - LGPL
37360  * <script type="text/javascript">
37361  */
37362 /**
37363  * @extends Roo.grid.AbstractSelectionModel
37364  * @class Roo.grid.RowSelectionModel
37365  * The default SelectionModel used by {@link Roo.grid.Grid}.
37366  * It supports multiple selections and keyboard selection/navigation. 
37367  * @constructor
37368  * @param {Object} config
37369  */
37370 Roo.grid.RowSelectionModel = function(config){
37371     Roo.apply(this, config);
37372     this.selections = new Roo.util.MixedCollection(false, function(o){
37373         return o.id;
37374     });
37375
37376     this.last = false;
37377     this.lastActive = false;
37378
37379     this.addEvents({
37380         /**
37381         * @event selectionchange
37382         * Fires when the selection changes
37383         * @param {SelectionModel} this
37384         */
37385        "selectionchange" : true,
37386        /**
37387         * @event afterselectionchange
37388         * Fires after the selection changes (eg. by key press or clicking)
37389         * @param {SelectionModel} this
37390         */
37391        "afterselectionchange" : true,
37392        /**
37393         * @event beforerowselect
37394         * Fires when a row is selected being selected, return false to cancel.
37395         * @param {SelectionModel} this
37396         * @param {Number} rowIndex The selected index
37397         * @param {Boolean} keepExisting False if other selections will be cleared
37398         */
37399        "beforerowselect" : true,
37400        /**
37401         * @event rowselect
37402         * Fires when a row is selected.
37403         * @param {SelectionModel} this
37404         * @param {Number} rowIndex The selected index
37405         * @param {Roo.data.Record} r The record
37406         */
37407        "rowselect" : true,
37408        /**
37409         * @event rowdeselect
37410         * Fires when a row is deselected.
37411         * @param {SelectionModel} this
37412         * @param {Number} rowIndex The selected index
37413         */
37414         "rowdeselect" : true
37415     });
37416     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
37417     this.locked = false;
37418 };
37419
37420 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
37421     /**
37422      * @cfg {Boolean} singleSelect
37423      * True to allow selection of only one row at a time (defaults to false)
37424      */
37425     singleSelect : false,
37426
37427     // private
37428     initEvents : function(){
37429
37430         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
37431             this.grid.on("mousedown", this.handleMouseDown, this);
37432         }else{ // allow click to work like normal
37433             this.grid.on("rowclick", this.handleDragableRowClick, this);
37434         }
37435         // bootstrap does not have a view..
37436         var view = this.grid.view ? this.grid.view : this.grid;
37437         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
37438             "up" : function(e){
37439                 if(!e.shiftKey){
37440                     this.selectPrevious(e.shiftKey);
37441                 }else if(this.last !== false && this.lastActive !== false){
37442                     var last = this.last;
37443                     this.selectRange(this.last,  this.lastActive-1);
37444                     view.focusRow(this.lastActive);
37445                     if(last !== false){
37446                         this.last = last;
37447                     }
37448                 }else{
37449                     this.selectFirstRow();
37450                 }
37451                 this.fireEvent("afterselectionchange", this);
37452             },
37453             "down" : function(e){
37454                 if(!e.shiftKey){
37455                     this.selectNext(e.shiftKey);
37456                 }else if(this.last !== false && this.lastActive !== false){
37457                     var last = this.last;
37458                     this.selectRange(this.last,  this.lastActive+1);
37459                     view.focusRow(this.lastActive);
37460                     if(last !== false){
37461                         this.last = last;
37462                     }
37463                 }else{
37464                     this.selectFirstRow();
37465                 }
37466                 this.fireEvent("afterselectionchange", this);
37467             },
37468             scope: this
37469         });
37470
37471          
37472         view.on("refresh", this.onRefresh, this);
37473         view.on("rowupdated", this.onRowUpdated, this);
37474         view.on("rowremoved", this.onRemove, this);
37475     },
37476
37477     // private
37478     onRefresh : function(){
37479         var ds = this.grid.ds, i, v = this.grid.view;
37480         var s = this.selections;
37481         s.each(function(r){
37482             if((i = ds.indexOfId(r.id)) != -1){
37483                 v.onRowSelect(i);
37484                 s.add(ds.getAt(i)); // updating the selection relate data
37485             }else{
37486                 s.remove(r);
37487             }
37488         });
37489     },
37490
37491     // private
37492     onRemove : function(v, index, r){
37493         this.selections.remove(r);
37494     },
37495
37496     // private
37497     onRowUpdated : function(v, index, r){
37498         if(this.isSelected(r)){
37499             v.onRowSelect(index);
37500         }
37501     },
37502
37503     /**
37504      * Select records.
37505      * @param {Array} records The records to select
37506      * @param {Boolean} keepExisting (optional) True to keep existing selections
37507      */
37508     selectRecords : function(records, keepExisting){
37509         if(!keepExisting){
37510             this.clearSelections();
37511         }
37512         var ds = this.grid.ds;
37513         for(var i = 0, len = records.length; i < len; i++){
37514             this.selectRow(ds.indexOf(records[i]), true);
37515         }
37516     },
37517
37518     /**
37519      * Gets the number of selected rows.
37520      * @return {Number}
37521      */
37522     getCount : function(){
37523         return this.selections.length;
37524     },
37525
37526     /**
37527      * Selects the first row in the grid.
37528      */
37529     selectFirstRow : function(){
37530         this.selectRow(0);
37531     },
37532
37533     /**
37534      * Select the last row.
37535      * @param {Boolean} keepExisting (optional) True to keep existing selections
37536      */
37537     selectLastRow : function(keepExisting){
37538         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
37539     },
37540
37541     /**
37542      * Selects the row immediately following the last selected row.
37543      * @param {Boolean} keepExisting (optional) True to keep existing selections
37544      */
37545     selectNext : function(keepExisting){
37546         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
37547             this.selectRow(this.last+1, keepExisting);
37548             var view = this.grid.view ? this.grid.view : this.grid;
37549             view.focusRow(this.last);
37550         }
37551     },
37552
37553     /**
37554      * Selects the row that precedes the last selected row.
37555      * @param {Boolean} keepExisting (optional) True to keep existing selections
37556      */
37557     selectPrevious : function(keepExisting){
37558         if(this.last){
37559             this.selectRow(this.last-1, keepExisting);
37560             var view = this.grid.view ? this.grid.view : this.grid;
37561             view.focusRow(this.last);
37562         }
37563     },
37564
37565     /**
37566      * Returns the selected records
37567      * @return {Array} Array of selected records
37568      */
37569     getSelections : function(){
37570         return [].concat(this.selections.items);
37571     },
37572
37573     /**
37574      * Returns the first selected record.
37575      * @return {Record}
37576      */
37577     getSelected : function(){
37578         return this.selections.itemAt(0);
37579     },
37580
37581
37582     /**
37583      * Clears all selections.
37584      */
37585     clearSelections : function(fast){
37586         if(this.locked) {
37587             return;
37588         }
37589         if(fast !== true){
37590             var ds = this.grid.ds;
37591             var s = this.selections;
37592             s.each(function(r){
37593                 this.deselectRow(ds.indexOfId(r.id));
37594             }, this);
37595             s.clear();
37596         }else{
37597             this.selections.clear();
37598         }
37599         this.last = false;
37600     },
37601
37602
37603     /**
37604      * Selects all rows.
37605      */
37606     selectAll : function(){
37607         if(this.locked) {
37608             return;
37609         }
37610         this.selections.clear();
37611         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
37612             this.selectRow(i, true);
37613         }
37614     },
37615
37616     /**
37617      * Returns True if there is a selection.
37618      * @return {Boolean}
37619      */
37620     hasSelection : function(){
37621         return this.selections.length > 0;
37622     },
37623
37624     /**
37625      * Returns True if the specified row is selected.
37626      * @param {Number/Record} record The record or index of the record to check
37627      * @return {Boolean}
37628      */
37629     isSelected : function(index){
37630         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
37631         return (r && this.selections.key(r.id) ? true : false);
37632     },
37633
37634     /**
37635      * Returns True if the specified record id is selected.
37636      * @param {String} id The id of record to check
37637      * @return {Boolean}
37638      */
37639     isIdSelected : function(id){
37640         return (this.selections.key(id) ? true : false);
37641     },
37642
37643     // private
37644     handleMouseDown : function(e, t)
37645     {
37646         var view = this.grid.view ? this.grid.view : this.grid;
37647         var rowIndex;
37648         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
37649             return;
37650         };
37651         if(e.shiftKey && this.last !== false){
37652             var last = this.last;
37653             this.selectRange(last, rowIndex, e.ctrlKey);
37654             this.last = last; // reset the last
37655             view.focusRow(rowIndex);
37656         }else{
37657             var isSelected = this.isSelected(rowIndex);
37658             if(e.button !== 0 && isSelected){
37659                 view.focusRow(rowIndex);
37660             }else if(e.ctrlKey && isSelected){
37661                 this.deselectRow(rowIndex);
37662             }else if(!isSelected){
37663                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
37664                 view.focusRow(rowIndex);
37665             }
37666         }
37667         this.fireEvent("afterselectionchange", this);
37668     },
37669     // private
37670     handleDragableRowClick :  function(grid, rowIndex, e) 
37671     {
37672         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
37673             this.selectRow(rowIndex, false);
37674             var view = this.grid.view ? this.grid.view : this.grid;
37675             view.focusRow(rowIndex);
37676              this.fireEvent("afterselectionchange", this);
37677         }
37678     },
37679     
37680     /**
37681      * Selects multiple rows.
37682      * @param {Array} rows Array of the indexes of the row to select
37683      * @param {Boolean} keepExisting (optional) True to keep existing selections
37684      */
37685     selectRows : function(rows, keepExisting){
37686         if(!keepExisting){
37687             this.clearSelections();
37688         }
37689         for(var i = 0, len = rows.length; i < len; i++){
37690             this.selectRow(rows[i], true);
37691         }
37692     },
37693
37694     /**
37695      * Selects a range of rows. All rows in between startRow and endRow are also selected.
37696      * @param {Number} startRow The index of the first row in the range
37697      * @param {Number} endRow The index of the last row in the range
37698      * @param {Boolean} keepExisting (optional) True to retain existing selections
37699      */
37700     selectRange : function(startRow, endRow, keepExisting){
37701         if(this.locked) {
37702             return;
37703         }
37704         if(!keepExisting){
37705             this.clearSelections();
37706         }
37707         if(startRow <= endRow){
37708             for(var i = startRow; i <= endRow; i++){
37709                 this.selectRow(i, true);
37710             }
37711         }else{
37712             for(var i = startRow; i >= endRow; i--){
37713                 this.selectRow(i, true);
37714             }
37715         }
37716     },
37717
37718     /**
37719      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
37720      * @param {Number} startRow The index of the first row in the range
37721      * @param {Number} endRow The index of the last row in the range
37722      */
37723     deselectRange : function(startRow, endRow, preventViewNotify){
37724         if(this.locked) {
37725             return;
37726         }
37727         for(var i = startRow; i <= endRow; i++){
37728             this.deselectRow(i, preventViewNotify);
37729         }
37730     },
37731
37732     /**
37733      * Selects a row.
37734      * @param {Number} row The index of the row to select
37735      * @param {Boolean} keepExisting (optional) True to keep existing selections
37736      */
37737     selectRow : function(index, keepExisting, preventViewNotify){
37738         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
37739             return;
37740         }
37741         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
37742             if(!keepExisting || this.singleSelect){
37743                 this.clearSelections();
37744             }
37745             var r = this.grid.ds.getAt(index);
37746             this.selections.add(r);
37747             this.last = this.lastActive = index;
37748             if(!preventViewNotify){
37749                 var view = this.grid.view ? this.grid.view : this.grid;
37750                 view.onRowSelect(index);
37751             }
37752             this.fireEvent("rowselect", this, index, r);
37753             this.fireEvent("selectionchange", this);
37754         }
37755     },
37756
37757     /**
37758      * Deselects a row.
37759      * @param {Number} row The index of the row to deselect
37760      */
37761     deselectRow : function(index, preventViewNotify){
37762         if(this.locked) {
37763             return;
37764         }
37765         if(this.last == index){
37766             this.last = false;
37767         }
37768         if(this.lastActive == index){
37769             this.lastActive = false;
37770         }
37771         var r = this.grid.ds.getAt(index);
37772         this.selections.remove(r);
37773         if(!preventViewNotify){
37774             var view = this.grid.view ? this.grid.view : this.grid;
37775             view.onRowDeselect(index);
37776         }
37777         this.fireEvent("rowdeselect", this, index);
37778         this.fireEvent("selectionchange", this);
37779     },
37780
37781     // private
37782     restoreLast : function(){
37783         if(this._last){
37784             this.last = this._last;
37785         }
37786     },
37787
37788     // private
37789     acceptsNav : function(row, col, cm){
37790         return !cm.isHidden(col) && cm.isCellEditable(col, row);
37791     },
37792
37793     // private
37794     onEditorKey : function(field, e){
37795         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
37796         if(k == e.TAB){
37797             e.stopEvent();
37798             ed.completeEdit();
37799             if(e.shiftKey){
37800                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
37801             }else{
37802                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37803             }
37804         }else if(k == e.ENTER && !e.ctrlKey){
37805             e.stopEvent();
37806             ed.completeEdit();
37807             if(e.shiftKey){
37808                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
37809             }else{
37810                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
37811             }
37812         }else if(k == e.ESC){
37813             ed.cancelEdit();
37814         }
37815         if(newCell){
37816             g.startEditing(newCell[0], newCell[1]);
37817         }
37818     }
37819 });/*
37820  * Based on:
37821  * Ext JS Library 1.1.1
37822  * Copyright(c) 2006-2007, Ext JS, LLC.
37823  *
37824  * Originally Released Under LGPL - original licence link has changed is not relivant.
37825  *
37826  * Fork - LGPL
37827  * <script type="text/javascript">
37828  */
37829 /**
37830  * @class Roo.grid.CellSelectionModel
37831  * @extends Roo.grid.AbstractSelectionModel
37832  * This class provides the basic implementation for cell selection in a grid.
37833  * @constructor
37834  * @param {Object} config The object containing the configuration of this model.
37835  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
37836  */
37837 Roo.grid.CellSelectionModel = function(config){
37838     Roo.apply(this, config);
37839
37840     this.selection = null;
37841
37842     this.addEvents({
37843         /**
37844              * @event beforerowselect
37845              * Fires before a cell is selected.
37846              * @param {SelectionModel} this
37847              * @param {Number} rowIndex The selected row index
37848              * @param {Number} colIndex The selected cell index
37849              */
37850             "beforecellselect" : true,
37851         /**
37852              * @event cellselect
37853              * Fires when a cell is selected.
37854              * @param {SelectionModel} this
37855              * @param {Number} rowIndex The selected row index
37856              * @param {Number} colIndex The selected cell index
37857              */
37858             "cellselect" : true,
37859         /**
37860              * @event selectionchange
37861              * Fires when the active selection changes.
37862              * @param {SelectionModel} this
37863              * @param {Object} selection null for no selection or an object (o) with two properties
37864                 <ul>
37865                 <li>o.record: the record object for the row the selection is in</li>
37866                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
37867                 </ul>
37868              */
37869             "selectionchange" : true,
37870         /**
37871              * @event tabend
37872              * Fires when the tab (or enter) was pressed on the last editable cell
37873              * You can use this to trigger add new row.
37874              * @param {SelectionModel} this
37875              */
37876             "tabend" : true,
37877          /**
37878              * @event beforeeditnext
37879              * Fires before the next editable sell is made active
37880              * You can use this to skip to another cell or fire the tabend
37881              *    if you set cell to false
37882              * @param {Object} eventdata object : { cell : [ row, col ] } 
37883              */
37884             "beforeeditnext" : true
37885     });
37886     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
37887 };
37888
37889 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
37890     
37891     enter_is_tab: false,
37892
37893     /** @ignore */
37894     initEvents : function(){
37895         this.grid.on("mousedown", this.handleMouseDown, this);
37896         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
37897         var view = this.grid.view;
37898         view.on("refresh", this.onViewChange, this);
37899         view.on("rowupdated", this.onRowUpdated, this);
37900         view.on("beforerowremoved", this.clearSelections, this);
37901         view.on("beforerowsinserted", this.clearSelections, this);
37902         if(this.grid.isEditor){
37903             this.grid.on("beforeedit", this.beforeEdit,  this);
37904         }
37905     },
37906
37907         //private
37908     beforeEdit : function(e){
37909         this.select(e.row, e.column, false, true, e.record);
37910     },
37911
37912         //private
37913     onRowUpdated : function(v, index, r){
37914         if(this.selection && this.selection.record == r){
37915             v.onCellSelect(index, this.selection.cell[1]);
37916         }
37917     },
37918
37919         //private
37920     onViewChange : function(){
37921         this.clearSelections(true);
37922     },
37923
37924         /**
37925          * Returns the currently selected cell,.
37926          * @return {Array} The selected cell (row, column) or null if none selected.
37927          */
37928     getSelectedCell : function(){
37929         return this.selection ? this.selection.cell : null;
37930     },
37931
37932     /**
37933      * Clears all selections.
37934      * @param {Boolean} true to prevent the gridview from being notified about the change.
37935      */
37936     clearSelections : function(preventNotify){
37937         var s = this.selection;
37938         if(s){
37939             if(preventNotify !== true){
37940                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
37941             }
37942             this.selection = null;
37943             this.fireEvent("selectionchange", this, null);
37944         }
37945     },
37946
37947     /**
37948      * Returns true if there is a selection.
37949      * @return {Boolean}
37950      */
37951     hasSelection : function(){
37952         return this.selection ? true : false;
37953     },
37954
37955     /** @ignore */
37956     handleMouseDown : function(e, t){
37957         var v = this.grid.getView();
37958         if(this.isLocked()){
37959             return;
37960         };
37961         var row = v.findRowIndex(t);
37962         var cell = v.findCellIndex(t);
37963         if(row !== false && cell !== false){
37964             this.select(row, cell);
37965         }
37966     },
37967
37968     /**
37969      * Selects a cell.
37970      * @param {Number} rowIndex
37971      * @param {Number} collIndex
37972      */
37973     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
37974         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
37975             this.clearSelections();
37976             r = r || this.grid.dataSource.getAt(rowIndex);
37977             this.selection = {
37978                 record : r,
37979                 cell : [rowIndex, colIndex]
37980             };
37981             if(!preventViewNotify){
37982                 var v = this.grid.getView();
37983                 v.onCellSelect(rowIndex, colIndex);
37984                 if(preventFocus !== true){
37985                     v.focusCell(rowIndex, colIndex);
37986                 }
37987             }
37988             this.fireEvent("cellselect", this, rowIndex, colIndex);
37989             this.fireEvent("selectionchange", this, this.selection);
37990         }
37991     },
37992
37993         //private
37994     isSelectable : function(rowIndex, colIndex, cm){
37995         return !cm.isHidden(colIndex);
37996     },
37997
37998     /** @ignore */
37999     handleKeyDown : function(e){
38000         //Roo.log('Cell Sel Model handleKeyDown');
38001         if(!e.isNavKeyPress()){
38002             return;
38003         }
38004         var g = this.grid, s = this.selection;
38005         if(!s){
38006             e.stopEvent();
38007             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
38008             if(cell){
38009                 this.select(cell[0], cell[1]);
38010             }
38011             return;
38012         }
38013         var sm = this;
38014         var walk = function(row, col, step){
38015             return g.walkCells(row, col, step, sm.isSelectable,  sm);
38016         };
38017         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
38018         var newCell;
38019
38020       
38021
38022         switch(k){
38023             case e.TAB:
38024                 // handled by onEditorKey
38025                 if (g.isEditor && g.editing) {
38026                     return;
38027                 }
38028                 if(e.shiftKey) {
38029                     newCell = walk(r, c-1, -1);
38030                 } else {
38031                     newCell = walk(r, c+1, 1);
38032                 }
38033                 break;
38034             
38035             case e.DOWN:
38036                newCell = walk(r+1, c, 1);
38037                 break;
38038             
38039             case e.UP:
38040                 newCell = walk(r-1, c, -1);
38041                 break;
38042             
38043             case e.RIGHT:
38044                 newCell = walk(r, c+1, 1);
38045                 break;
38046             
38047             case e.LEFT:
38048                 newCell = walk(r, c-1, -1);
38049                 break;
38050             
38051             case e.ENTER:
38052                 
38053                 if(g.isEditor && !g.editing){
38054                    g.startEditing(r, c);
38055                    e.stopEvent();
38056                    return;
38057                 }
38058                 
38059                 
38060              break;
38061         };
38062         if(newCell){
38063             this.select(newCell[0], newCell[1]);
38064             e.stopEvent();
38065             
38066         }
38067     },
38068
38069     acceptsNav : function(row, col, cm){
38070         return !cm.isHidden(col) && cm.isCellEditable(col, row);
38071     },
38072     /**
38073      * Selects a cell.
38074      * @param {Number} field (not used) - as it's normally used as a listener
38075      * @param {Number} e - event - fake it by using
38076      *
38077      * var e = Roo.EventObjectImpl.prototype;
38078      * e.keyCode = e.TAB
38079      *
38080      * 
38081      */
38082     onEditorKey : function(field, e){
38083         
38084         var k = e.getKey(),
38085             newCell,
38086             g = this.grid,
38087             ed = g.activeEditor,
38088             forward = false;
38089         ///Roo.log('onEditorKey' + k);
38090         
38091         
38092         if (this.enter_is_tab && k == e.ENTER) {
38093             k = e.TAB;
38094         }
38095         
38096         if(k == e.TAB){
38097             if(e.shiftKey){
38098                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
38099             }else{
38100                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38101                 forward = true;
38102             }
38103             
38104             e.stopEvent();
38105             
38106         } else if(k == e.ENTER &&  !e.ctrlKey){
38107             ed.completeEdit();
38108             e.stopEvent();
38109             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38110         
38111                 } else if(k == e.ESC){
38112             ed.cancelEdit();
38113         }
38114                 
38115         if (newCell) {
38116             var ecall = { cell : newCell, forward : forward };
38117             this.fireEvent('beforeeditnext', ecall );
38118             newCell = ecall.cell;
38119                         forward = ecall.forward;
38120         }
38121                 
38122         if(newCell){
38123             //Roo.log('next cell after edit');
38124             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
38125         } else if (forward) {
38126             // tabbed past last
38127             this.fireEvent.defer(100, this, ['tabend',this]);
38128         }
38129     }
38130 });/*
38131  * Based on:
38132  * Ext JS Library 1.1.1
38133  * Copyright(c) 2006-2007, Ext JS, LLC.
38134  *
38135  * Originally Released Under LGPL - original licence link has changed is not relivant.
38136  *
38137  * Fork - LGPL
38138  * <script type="text/javascript">
38139  */
38140  
38141 /**
38142  * @class Roo.grid.EditorGrid
38143  * @extends Roo.grid.Grid
38144  * Class for creating and editable grid.
38145  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
38146  * The container MUST have some type of size defined for the grid to fill. The container will be 
38147  * automatically set to position relative if it isn't already.
38148  * @param {Object} dataSource The data model to bind to
38149  * @param {Object} colModel The column model with info about this grid's columns
38150  */
38151 Roo.grid.EditorGrid = function(container, config){
38152     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
38153     this.getGridEl().addClass("xedit-grid");
38154
38155     if(!this.selModel){
38156         this.selModel = new Roo.grid.CellSelectionModel();
38157     }
38158
38159     this.activeEditor = null;
38160
38161         this.addEvents({
38162             /**
38163              * @event beforeedit
38164              * Fires before cell editing is triggered. The edit event object has the following properties <br />
38165              * <ul style="padding:5px;padding-left:16px;">
38166              * <li>grid - This grid</li>
38167              * <li>record - The record being edited</li>
38168              * <li>field - The field name being edited</li>
38169              * <li>value - The value for the field being edited.</li>
38170              * <li>row - The grid row index</li>
38171              * <li>column - The grid column index</li>
38172              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38173              * </ul>
38174              * @param {Object} e An edit event (see above for description)
38175              */
38176             "beforeedit" : true,
38177             /**
38178              * @event afteredit
38179              * Fires after a cell is edited. <br />
38180              * <ul style="padding:5px;padding-left:16px;">
38181              * <li>grid - This grid</li>
38182              * <li>record - The record being edited</li>
38183              * <li>field - The field name being edited</li>
38184              * <li>value - The value being set</li>
38185              * <li>originalValue - The original value for the field, before the edit.</li>
38186              * <li>row - The grid row index</li>
38187              * <li>column - The grid column index</li>
38188              * </ul>
38189              * @param {Object} e An edit event (see above for description)
38190              */
38191             "afteredit" : true,
38192             /**
38193              * @event validateedit
38194              * Fires after a cell is edited, but before the value is set in the record. 
38195          * You can use this to modify the value being set in the field, Return false
38196              * to cancel the change. The edit event object has the following properties <br />
38197              * <ul style="padding:5px;padding-left:16px;">
38198          * <li>editor - This editor</li>
38199              * <li>grid - This grid</li>
38200              * <li>record - The record being edited</li>
38201              * <li>field - The field name being edited</li>
38202              * <li>value - The value being set</li>
38203              * <li>originalValue - The original value for the field, before the edit.</li>
38204              * <li>row - The grid row index</li>
38205              * <li>column - The grid column index</li>
38206              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38207              * </ul>
38208              * @param {Object} e An edit event (see above for description)
38209              */
38210             "validateedit" : true
38211         });
38212     this.on("bodyscroll", this.stopEditing,  this);
38213     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
38214 };
38215
38216 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
38217     /**
38218      * @cfg {Number} clicksToEdit
38219      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
38220      */
38221     clicksToEdit: 2,
38222
38223     // private
38224     isEditor : true,
38225     // private
38226     trackMouseOver: false, // causes very odd FF errors
38227
38228     onCellDblClick : function(g, row, col){
38229         this.startEditing(row, col);
38230     },
38231
38232     onEditComplete : function(ed, value, startValue){
38233         this.editing = false;
38234         this.activeEditor = null;
38235         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
38236         var r = ed.record;
38237         var field = this.colModel.getDataIndex(ed.col);
38238         var e = {
38239             grid: this,
38240             record: r,
38241             field: field,
38242             originalValue: startValue,
38243             value: value,
38244             row: ed.row,
38245             column: ed.col,
38246             cancel:false,
38247             editor: ed
38248         };
38249         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
38250         cell.show();
38251           
38252         if(String(value) !== String(startValue)){
38253             
38254             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
38255                 r.set(field, e.value);
38256                 // if we are dealing with a combo box..
38257                 // then we also set the 'name' colum to be the displayField
38258                 if (ed.field.displayField && ed.field.name) {
38259                     r.set(ed.field.name, ed.field.el.dom.value);
38260                 }
38261                 
38262                 delete e.cancel; //?? why!!!
38263                 this.fireEvent("afteredit", e);
38264             }
38265         } else {
38266             this.fireEvent("afteredit", e); // always fire it!
38267         }
38268         this.view.focusCell(ed.row, ed.col);
38269     },
38270
38271     /**
38272      * Starts editing the specified for the specified row/column
38273      * @param {Number} rowIndex
38274      * @param {Number} colIndex
38275      */
38276     startEditing : function(row, col){
38277         this.stopEditing();
38278         if(this.colModel.isCellEditable(col, row)){
38279             this.view.ensureVisible(row, col, true);
38280           
38281             var r = this.dataSource.getAt(row);
38282             var field = this.colModel.getDataIndex(col);
38283             var cell = Roo.get(this.view.getCell(row,col));
38284             var e = {
38285                 grid: this,
38286                 record: r,
38287                 field: field,
38288                 value: r.data[field],
38289                 row: row,
38290                 column: col,
38291                 cancel:false 
38292             };
38293             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
38294                 this.editing = true;
38295                 var ed = this.colModel.getCellEditor(col, row);
38296                 
38297                 if (!ed) {
38298                     return;
38299                 }
38300                 if(!ed.rendered){
38301                     ed.render(ed.parentEl || document.body);
38302                 }
38303                 ed.field.reset();
38304                
38305                 cell.hide();
38306                 
38307                 (function(){ // complex but required for focus issues in safari, ie and opera
38308                     ed.row = row;
38309                     ed.col = col;
38310                     ed.record = r;
38311                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
38312                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
38313                     this.activeEditor = ed;
38314                     var v = r.data[field];
38315                     ed.startEdit(this.view.getCell(row, col), v);
38316                     // combo's with 'displayField and name set
38317                     if (ed.field.displayField && ed.field.name) {
38318                         ed.field.el.dom.value = r.data[ed.field.name];
38319                     }
38320                     
38321                     
38322                 }).defer(50, this);
38323             }
38324         }
38325     },
38326         
38327     /**
38328      * Stops any active editing
38329      */
38330     stopEditing : function(){
38331         if(this.activeEditor){
38332             this.activeEditor.completeEdit();
38333         }
38334         this.activeEditor = null;
38335     },
38336         
38337          /**
38338      * Called to get grid's drag proxy text, by default returns this.ddText.
38339      * @return {String}
38340      */
38341     getDragDropText : function(){
38342         var count = this.selModel.getSelectedCell() ? 1 : 0;
38343         return String.format(this.ddText, count, count == 1 ? '' : 's');
38344     }
38345         
38346 });/*
38347  * Based on:
38348  * Ext JS Library 1.1.1
38349  * Copyright(c) 2006-2007, Ext JS, LLC.
38350  *
38351  * Originally Released Under LGPL - original licence link has changed is not relivant.
38352  *
38353  * Fork - LGPL
38354  * <script type="text/javascript">
38355  */
38356
38357 // private - not really -- you end up using it !
38358 // This is a support class used internally by the Grid components
38359
38360 /**
38361  * @class Roo.grid.GridEditor
38362  * @extends Roo.Editor
38363  * Class for creating and editable grid elements.
38364  * @param {Object} config any settings (must include field)
38365  */
38366 Roo.grid.GridEditor = function(field, config){
38367     if (!config && field.field) {
38368         config = field;
38369         field = Roo.factory(config.field, Roo.form);
38370     }
38371     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
38372     field.monitorTab = false;
38373 };
38374
38375 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
38376     
38377     /**
38378      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
38379      */
38380     
38381     alignment: "tl-tl",
38382     autoSize: "width",
38383     hideEl : false,
38384     cls: "x-small-editor x-grid-editor",
38385     shim:false,
38386     shadow:"frame"
38387 });/*
38388  * Based on:
38389  * Ext JS Library 1.1.1
38390  * Copyright(c) 2006-2007, Ext JS, LLC.
38391  *
38392  * Originally Released Under LGPL - original licence link has changed is not relivant.
38393  *
38394  * Fork - LGPL
38395  * <script type="text/javascript">
38396  */
38397   
38398
38399   
38400 Roo.grid.PropertyRecord = Roo.data.Record.create([
38401     {name:'name',type:'string'},  'value'
38402 ]);
38403
38404
38405 Roo.grid.PropertyStore = function(grid, source){
38406     this.grid = grid;
38407     this.store = new Roo.data.Store({
38408         recordType : Roo.grid.PropertyRecord
38409     });
38410     this.store.on('update', this.onUpdate,  this);
38411     if(source){
38412         this.setSource(source);
38413     }
38414     Roo.grid.PropertyStore.superclass.constructor.call(this);
38415 };
38416
38417
38418
38419 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
38420     setSource : function(o){
38421         this.source = o;
38422         this.store.removeAll();
38423         var data = [];
38424         for(var k in o){
38425             if(this.isEditableValue(o[k])){
38426                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
38427             }
38428         }
38429         this.store.loadRecords({records: data}, {}, true);
38430     },
38431
38432     onUpdate : function(ds, record, type){
38433         if(type == Roo.data.Record.EDIT){
38434             var v = record.data['value'];
38435             var oldValue = record.modified['value'];
38436             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
38437                 this.source[record.id] = v;
38438                 record.commit();
38439                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
38440             }else{
38441                 record.reject();
38442             }
38443         }
38444     },
38445
38446     getProperty : function(row){
38447        return this.store.getAt(row);
38448     },
38449
38450     isEditableValue: function(val){
38451         if(val && val instanceof Date){
38452             return true;
38453         }else if(typeof val == 'object' || typeof val == 'function'){
38454             return false;
38455         }
38456         return true;
38457     },
38458
38459     setValue : function(prop, value){
38460         this.source[prop] = value;
38461         this.store.getById(prop).set('value', value);
38462     },
38463
38464     getSource : function(){
38465         return this.source;
38466     }
38467 });
38468
38469 Roo.grid.PropertyColumnModel = function(grid, store){
38470     this.grid = grid;
38471     var g = Roo.grid;
38472     g.PropertyColumnModel.superclass.constructor.call(this, [
38473         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
38474         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
38475     ]);
38476     this.store = store;
38477     this.bselect = Roo.DomHelper.append(document.body, {
38478         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
38479             {tag: 'option', value: 'true', html: 'true'},
38480             {tag: 'option', value: 'false', html: 'false'}
38481         ]
38482     });
38483     Roo.id(this.bselect);
38484     var f = Roo.form;
38485     this.editors = {
38486         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
38487         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
38488         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
38489         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
38490         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
38491     };
38492     this.renderCellDelegate = this.renderCell.createDelegate(this);
38493     this.renderPropDelegate = this.renderProp.createDelegate(this);
38494 };
38495
38496 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
38497     
38498     
38499     nameText : 'Name',
38500     valueText : 'Value',
38501     
38502     dateFormat : 'm/j/Y',
38503     
38504     
38505     renderDate : function(dateVal){
38506         return dateVal.dateFormat(this.dateFormat);
38507     },
38508
38509     renderBool : function(bVal){
38510         return bVal ? 'true' : 'false';
38511     },
38512
38513     isCellEditable : function(colIndex, rowIndex){
38514         return colIndex == 1;
38515     },
38516
38517     getRenderer : function(col){
38518         return col == 1 ?
38519             this.renderCellDelegate : this.renderPropDelegate;
38520     },
38521
38522     renderProp : function(v){
38523         return this.getPropertyName(v);
38524     },
38525
38526     renderCell : function(val){
38527         var rv = val;
38528         if(val instanceof Date){
38529             rv = this.renderDate(val);
38530         }else if(typeof val == 'boolean'){
38531             rv = this.renderBool(val);
38532         }
38533         return Roo.util.Format.htmlEncode(rv);
38534     },
38535
38536     getPropertyName : function(name){
38537         var pn = this.grid.propertyNames;
38538         return pn && pn[name] ? pn[name] : name;
38539     },
38540
38541     getCellEditor : function(colIndex, rowIndex){
38542         var p = this.store.getProperty(rowIndex);
38543         var n = p.data['name'], val = p.data['value'];
38544         
38545         if(typeof(this.grid.customEditors[n]) == 'string'){
38546             return this.editors[this.grid.customEditors[n]];
38547         }
38548         if(typeof(this.grid.customEditors[n]) != 'undefined'){
38549             return this.grid.customEditors[n];
38550         }
38551         if(val instanceof Date){
38552             return this.editors['date'];
38553         }else if(typeof val == 'number'){
38554             return this.editors['number'];
38555         }else if(typeof val == 'boolean'){
38556             return this.editors['boolean'];
38557         }else{
38558             return this.editors['string'];
38559         }
38560     }
38561 });
38562
38563 /**
38564  * @class Roo.grid.PropertyGrid
38565  * @extends Roo.grid.EditorGrid
38566  * This class represents the  interface of a component based property grid control.
38567  * <br><br>Usage:<pre><code>
38568  var grid = new Roo.grid.PropertyGrid("my-container-id", {
38569       
38570  });
38571  // set any options
38572  grid.render();
38573  * </code></pre>
38574   
38575  * @constructor
38576  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
38577  * The container MUST have some type of size defined for the grid to fill. The container will be
38578  * automatically set to position relative if it isn't already.
38579  * @param {Object} config A config object that sets properties on this grid.
38580  */
38581 Roo.grid.PropertyGrid = function(container, config){
38582     config = config || {};
38583     var store = new Roo.grid.PropertyStore(this);
38584     this.store = store;
38585     var cm = new Roo.grid.PropertyColumnModel(this, store);
38586     store.store.sort('name', 'ASC');
38587     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
38588         ds: store.store,
38589         cm: cm,
38590         enableColLock:false,
38591         enableColumnMove:false,
38592         stripeRows:false,
38593         trackMouseOver: false,
38594         clicksToEdit:1
38595     }, config));
38596     this.getGridEl().addClass('x-props-grid');
38597     this.lastEditRow = null;
38598     this.on('columnresize', this.onColumnResize, this);
38599     this.addEvents({
38600          /**
38601              * @event beforepropertychange
38602              * Fires before a property changes (return false to stop?)
38603              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38604              * @param {String} id Record Id
38605              * @param {String} newval New Value
38606          * @param {String} oldval Old Value
38607              */
38608         "beforepropertychange": true,
38609         /**
38610              * @event propertychange
38611              * Fires after a property changes
38612              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38613              * @param {String} id Record Id
38614              * @param {String} newval New Value
38615          * @param {String} oldval Old Value
38616              */
38617         "propertychange": true
38618     });
38619     this.customEditors = this.customEditors || {};
38620 };
38621 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
38622     
38623      /**
38624      * @cfg {Object} customEditors map of colnames=> custom editors.
38625      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
38626      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
38627      * false disables editing of the field.
38628          */
38629     
38630       /**
38631      * @cfg {Object} propertyNames map of property Names to their displayed value
38632          */
38633     
38634     render : function(){
38635         Roo.grid.PropertyGrid.superclass.render.call(this);
38636         this.autoSize.defer(100, this);
38637     },
38638
38639     autoSize : function(){
38640         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
38641         if(this.view){
38642             this.view.fitColumns();
38643         }
38644     },
38645
38646     onColumnResize : function(){
38647         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
38648         this.autoSize();
38649     },
38650     /**
38651      * Sets the data for the Grid
38652      * accepts a Key => Value object of all the elements avaiable.
38653      * @param {Object} data  to appear in grid.
38654      */
38655     setSource : function(source){
38656         this.store.setSource(source);
38657         //this.autoSize();
38658     },
38659     /**
38660      * Gets all the data from the grid.
38661      * @return {Object} data  data stored in grid
38662      */
38663     getSource : function(){
38664         return this.store.getSource();
38665     }
38666 });/*
38667   
38668  * Licence LGPL
38669  
38670  */
38671  
38672 /**
38673  * @class Roo.grid.Calendar
38674  * @extends Roo.grid.Grid
38675  * This class extends the Grid to provide a calendar widget
38676  * <br><br>Usage:<pre><code>
38677  var grid = new Roo.grid.Calendar("my-container-id", {
38678      ds: myDataStore,
38679      cm: myColModel,
38680      selModel: mySelectionModel,
38681      autoSizeColumns: true,
38682      monitorWindowResize: false,
38683      trackMouseOver: true
38684      eventstore : real data store..
38685  });
38686  // set any options
38687  grid.render();
38688   
38689   * @constructor
38690  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
38691  * The container MUST have some type of size defined for the grid to fill. The container will be
38692  * automatically set to position relative if it isn't already.
38693  * @param {Object} config A config object that sets properties on this grid.
38694  */
38695 Roo.grid.Calendar = function(container, config){
38696         // initialize the container
38697         this.container = Roo.get(container);
38698         this.container.update("");
38699         this.container.setStyle("overflow", "hidden");
38700     this.container.addClass('x-grid-container');
38701
38702     this.id = this.container.id;
38703
38704     Roo.apply(this, config);
38705     // check and correct shorthanded configs
38706     
38707     var rows = [];
38708     var d =1;
38709     for (var r = 0;r < 6;r++) {
38710         
38711         rows[r]=[];
38712         for (var c =0;c < 7;c++) {
38713             rows[r][c]= '';
38714         }
38715     }
38716     if (this.eventStore) {
38717         this.eventStore= Roo.factory(this.eventStore, Roo.data);
38718         this.eventStore.on('load',this.onLoad, this);
38719         this.eventStore.on('beforeload',this.clearEvents, this);
38720          
38721     }
38722     
38723     this.dataSource = new Roo.data.Store({
38724             proxy: new Roo.data.MemoryProxy(rows),
38725             reader: new Roo.data.ArrayReader({}, [
38726                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
38727     });
38728
38729     this.dataSource.load();
38730     this.ds = this.dataSource;
38731     this.ds.xmodule = this.xmodule || false;
38732     
38733     
38734     var cellRender = function(v,x,r)
38735     {
38736         return String.format(
38737             '<div class="fc-day  fc-widget-content"><div>' +
38738                 '<div class="fc-event-container"></div>' +
38739                 '<div class="fc-day-number">{0}</div>'+
38740                 
38741                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
38742             '</div></div>', v);
38743     
38744     }
38745     
38746     
38747     this.colModel = new Roo.grid.ColumnModel( [
38748         {
38749             xtype: 'ColumnModel',
38750             xns: Roo.grid,
38751             dataIndex : 'weekday0',
38752             header : 'Sunday',
38753             renderer : cellRender
38754         },
38755         {
38756             xtype: 'ColumnModel',
38757             xns: Roo.grid,
38758             dataIndex : 'weekday1',
38759             header : 'Monday',
38760             renderer : cellRender
38761         },
38762         {
38763             xtype: 'ColumnModel',
38764             xns: Roo.grid,
38765             dataIndex : 'weekday2',
38766             header : 'Tuesday',
38767             renderer : cellRender
38768         },
38769         {
38770             xtype: 'ColumnModel',
38771             xns: Roo.grid,
38772             dataIndex : 'weekday3',
38773             header : 'Wednesday',
38774             renderer : cellRender
38775         },
38776         {
38777             xtype: 'ColumnModel',
38778             xns: Roo.grid,
38779             dataIndex : 'weekday4',
38780             header : 'Thursday',
38781             renderer : cellRender
38782         },
38783         {
38784             xtype: 'ColumnModel',
38785             xns: Roo.grid,
38786             dataIndex : 'weekday5',
38787             header : 'Friday',
38788             renderer : cellRender
38789         },
38790         {
38791             xtype: 'ColumnModel',
38792             xns: Roo.grid,
38793             dataIndex : 'weekday6',
38794             header : 'Saturday',
38795             renderer : cellRender
38796         }
38797     ]);
38798     this.cm = this.colModel;
38799     this.cm.xmodule = this.xmodule || false;
38800  
38801         
38802           
38803     //this.selModel = new Roo.grid.CellSelectionModel();
38804     //this.sm = this.selModel;
38805     //this.selModel.init(this);
38806     
38807     
38808     if(this.width){
38809         this.container.setWidth(this.width);
38810     }
38811
38812     if(this.height){
38813         this.container.setHeight(this.height);
38814     }
38815     /** @private */
38816         this.addEvents({
38817         // raw events
38818         /**
38819          * @event click
38820          * The raw click event for the entire grid.
38821          * @param {Roo.EventObject} e
38822          */
38823         "click" : true,
38824         /**
38825          * @event dblclick
38826          * The raw dblclick event for the entire grid.
38827          * @param {Roo.EventObject} e
38828          */
38829         "dblclick" : true,
38830         /**
38831          * @event contextmenu
38832          * The raw contextmenu event for the entire grid.
38833          * @param {Roo.EventObject} e
38834          */
38835         "contextmenu" : true,
38836         /**
38837          * @event mousedown
38838          * The raw mousedown event for the entire grid.
38839          * @param {Roo.EventObject} e
38840          */
38841         "mousedown" : true,
38842         /**
38843          * @event mouseup
38844          * The raw mouseup event for the entire grid.
38845          * @param {Roo.EventObject} e
38846          */
38847         "mouseup" : true,
38848         /**
38849          * @event mouseover
38850          * The raw mouseover event for the entire grid.
38851          * @param {Roo.EventObject} e
38852          */
38853         "mouseover" : true,
38854         /**
38855          * @event mouseout
38856          * The raw mouseout event for the entire grid.
38857          * @param {Roo.EventObject} e
38858          */
38859         "mouseout" : true,
38860         /**
38861          * @event keypress
38862          * The raw keypress event for the entire grid.
38863          * @param {Roo.EventObject} e
38864          */
38865         "keypress" : true,
38866         /**
38867          * @event keydown
38868          * The raw keydown event for the entire grid.
38869          * @param {Roo.EventObject} e
38870          */
38871         "keydown" : true,
38872
38873         // custom events
38874
38875         /**
38876          * @event cellclick
38877          * Fires when a cell is clicked
38878          * @param {Grid} this
38879          * @param {Number} rowIndex
38880          * @param {Number} columnIndex
38881          * @param {Roo.EventObject} e
38882          */
38883         "cellclick" : true,
38884         /**
38885          * @event celldblclick
38886          * Fires when a cell is double clicked
38887          * @param {Grid} this
38888          * @param {Number} rowIndex
38889          * @param {Number} columnIndex
38890          * @param {Roo.EventObject} e
38891          */
38892         "celldblclick" : true,
38893         /**
38894          * @event rowclick
38895          * Fires when a row is clicked
38896          * @param {Grid} this
38897          * @param {Number} rowIndex
38898          * @param {Roo.EventObject} e
38899          */
38900         "rowclick" : true,
38901         /**
38902          * @event rowdblclick
38903          * Fires when a row is double clicked
38904          * @param {Grid} this
38905          * @param {Number} rowIndex
38906          * @param {Roo.EventObject} e
38907          */
38908         "rowdblclick" : true,
38909         /**
38910          * @event headerclick
38911          * Fires when a header is clicked
38912          * @param {Grid} this
38913          * @param {Number} columnIndex
38914          * @param {Roo.EventObject} e
38915          */
38916         "headerclick" : true,
38917         /**
38918          * @event headerdblclick
38919          * Fires when a header cell is double clicked
38920          * @param {Grid} this
38921          * @param {Number} columnIndex
38922          * @param {Roo.EventObject} e
38923          */
38924         "headerdblclick" : true,
38925         /**
38926          * @event rowcontextmenu
38927          * Fires when a row is right clicked
38928          * @param {Grid} this
38929          * @param {Number} rowIndex
38930          * @param {Roo.EventObject} e
38931          */
38932         "rowcontextmenu" : true,
38933         /**
38934          * @event cellcontextmenu
38935          * Fires when a cell is right clicked
38936          * @param {Grid} this
38937          * @param {Number} rowIndex
38938          * @param {Number} cellIndex
38939          * @param {Roo.EventObject} e
38940          */
38941          "cellcontextmenu" : true,
38942         /**
38943          * @event headercontextmenu
38944          * Fires when a header is right clicked
38945          * @param {Grid} this
38946          * @param {Number} columnIndex
38947          * @param {Roo.EventObject} e
38948          */
38949         "headercontextmenu" : true,
38950         /**
38951          * @event bodyscroll
38952          * Fires when the body element is scrolled
38953          * @param {Number} scrollLeft
38954          * @param {Number} scrollTop
38955          */
38956         "bodyscroll" : true,
38957         /**
38958          * @event columnresize
38959          * Fires when the user resizes a column
38960          * @param {Number} columnIndex
38961          * @param {Number} newSize
38962          */
38963         "columnresize" : true,
38964         /**
38965          * @event columnmove
38966          * Fires when the user moves a column
38967          * @param {Number} oldIndex
38968          * @param {Number} newIndex
38969          */
38970         "columnmove" : true,
38971         /**
38972          * @event startdrag
38973          * Fires when row(s) start being dragged
38974          * @param {Grid} this
38975          * @param {Roo.GridDD} dd The drag drop object
38976          * @param {event} e The raw browser event
38977          */
38978         "startdrag" : true,
38979         /**
38980          * @event enddrag
38981          * Fires when a drag operation is complete
38982          * @param {Grid} this
38983          * @param {Roo.GridDD} dd The drag drop object
38984          * @param {event} e The raw browser event
38985          */
38986         "enddrag" : true,
38987         /**
38988          * @event dragdrop
38989          * Fires when dragged row(s) are dropped on a valid DD target
38990          * @param {Grid} this
38991          * @param {Roo.GridDD} dd The drag drop object
38992          * @param {String} targetId The target drag drop object
38993          * @param {event} e The raw browser event
38994          */
38995         "dragdrop" : true,
38996         /**
38997          * @event dragover
38998          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
38999          * @param {Grid} this
39000          * @param {Roo.GridDD} dd The drag drop object
39001          * @param {String} targetId The target drag drop object
39002          * @param {event} e The raw browser event
39003          */
39004         "dragover" : true,
39005         /**
39006          * @event dragenter
39007          *  Fires when the dragged row(s) first cross another DD target while being dragged
39008          * @param {Grid} this
39009          * @param {Roo.GridDD} dd The drag drop object
39010          * @param {String} targetId The target drag drop object
39011          * @param {event} e The raw browser event
39012          */
39013         "dragenter" : true,
39014         /**
39015          * @event dragout
39016          * Fires when the dragged row(s) leave another DD target while being dragged
39017          * @param {Grid} this
39018          * @param {Roo.GridDD} dd The drag drop object
39019          * @param {String} targetId The target drag drop object
39020          * @param {event} e The raw browser event
39021          */
39022         "dragout" : true,
39023         /**
39024          * @event rowclass
39025          * Fires when a row is rendered, so you can change add a style to it.
39026          * @param {GridView} gridview   The grid view
39027          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
39028          */
39029         'rowclass' : true,
39030
39031         /**
39032          * @event render
39033          * Fires when the grid is rendered
39034          * @param {Grid} grid
39035          */
39036         'render' : true,
39037             /**
39038              * @event select
39039              * Fires when a date is selected
39040              * @param {DatePicker} this
39041              * @param {Date} date The selected date
39042              */
39043         'select': true,
39044         /**
39045              * @event monthchange
39046              * Fires when the displayed month changes 
39047              * @param {DatePicker} this
39048              * @param {Date} date The selected month
39049              */
39050         'monthchange': true,
39051         /**
39052              * @event evententer
39053              * Fires when mouse over an event
39054              * @param {Calendar} this
39055              * @param {event} Event
39056              */
39057         'evententer': true,
39058         /**
39059              * @event eventleave
39060              * Fires when the mouse leaves an
39061              * @param {Calendar} this
39062              * @param {event}
39063              */
39064         'eventleave': true,
39065         /**
39066              * @event eventclick
39067              * Fires when the mouse click an
39068              * @param {Calendar} this
39069              * @param {event}
39070              */
39071         'eventclick': true,
39072         /**
39073              * @event eventrender
39074              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
39075              * @param {Calendar} this
39076              * @param {data} data to be modified
39077              */
39078         'eventrender': true
39079         
39080     });
39081
39082     Roo.grid.Grid.superclass.constructor.call(this);
39083     this.on('render', function() {
39084         this.view.el.addClass('x-grid-cal'); 
39085         
39086         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
39087
39088     },this);
39089     
39090     if (!Roo.grid.Calendar.style) {
39091         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
39092             
39093             
39094             '.x-grid-cal .x-grid-col' :  {
39095                 height: 'auto !important',
39096                 'vertical-align': 'top'
39097             },
39098             '.x-grid-cal  .fc-event-hori' : {
39099                 height: '14px'
39100             }
39101              
39102             
39103         }, Roo.id());
39104     }
39105
39106     
39107     
39108 };
39109 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
39110     /**
39111      * @cfg {Store} eventStore The store that loads events.
39112      */
39113     eventStore : 25,
39114
39115      
39116     activeDate : false,
39117     startDay : 0,
39118     autoWidth : true,
39119     monitorWindowResize : false,
39120
39121     
39122     resizeColumns : function() {
39123         var col = (this.view.el.getWidth() / 7) - 3;
39124         // loop through cols, and setWidth
39125         for(var i =0 ; i < 7 ; i++){
39126             this.cm.setColumnWidth(i, col);
39127         }
39128     },
39129      setDate :function(date) {
39130         
39131         Roo.log('setDate?');
39132         
39133         this.resizeColumns();
39134         var vd = this.activeDate;
39135         this.activeDate = date;
39136 //        if(vd && this.el){
39137 //            var t = date.getTime();
39138 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
39139 //                Roo.log('using add remove');
39140 //                
39141 //                this.fireEvent('monthchange', this, date);
39142 //                
39143 //                this.cells.removeClass("fc-state-highlight");
39144 //                this.cells.each(function(c){
39145 //                   if(c.dateValue == t){
39146 //                       c.addClass("fc-state-highlight");
39147 //                       setTimeout(function(){
39148 //                            try{c.dom.firstChild.focus();}catch(e){}
39149 //                       }, 50);
39150 //                       return false;
39151 //                   }
39152 //                   return true;
39153 //                });
39154 //                return;
39155 //            }
39156 //        }
39157         
39158         var days = date.getDaysInMonth();
39159         
39160         var firstOfMonth = date.getFirstDateOfMonth();
39161         var startingPos = firstOfMonth.getDay()-this.startDay;
39162         
39163         if(startingPos < this.startDay){
39164             startingPos += 7;
39165         }
39166         
39167         var pm = date.add(Date.MONTH, -1);
39168         var prevStart = pm.getDaysInMonth()-startingPos;
39169 //        
39170         
39171         
39172         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
39173         
39174         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
39175         //this.cells.addClassOnOver('fc-state-hover');
39176         
39177         var cells = this.cells.elements;
39178         var textEls = this.textNodes;
39179         
39180         //Roo.each(cells, function(cell){
39181         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
39182         //});
39183         
39184         days += startingPos;
39185
39186         // convert everything to numbers so it's fast
39187         var day = 86400000;
39188         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
39189         //Roo.log(d);
39190         //Roo.log(pm);
39191         //Roo.log(prevStart);
39192         
39193         var today = new Date().clearTime().getTime();
39194         var sel = date.clearTime().getTime();
39195         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
39196         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
39197         var ddMatch = this.disabledDatesRE;
39198         var ddText = this.disabledDatesText;
39199         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
39200         var ddaysText = this.disabledDaysText;
39201         var format = this.format;
39202         
39203         var setCellClass = function(cal, cell){
39204             
39205             //Roo.log('set Cell Class');
39206             cell.title = "";
39207             var t = d.getTime();
39208             
39209             //Roo.log(d);
39210             
39211             
39212             cell.dateValue = t;
39213             if(t == today){
39214                 cell.className += " fc-today";
39215                 cell.className += " fc-state-highlight";
39216                 cell.title = cal.todayText;
39217             }
39218             if(t == sel){
39219                 // disable highlight in other month..
39220                 cell.className += " fc-state-highlight";
39221                 
39222             }
39223             // disabling
39224             if(t < min) {
39225                 //cell.className = " fc-state-disabled";
39226                 cell.title = cal.minText;
39227                 return;
39228             }
39229             if(t > max) {
39230                 //cell.className = " fc-state-disabled";
39231                 cell.title = cal.maxText;
39232                 return;
39233             }
39234             if(ddays){
39235                 if(ddays.indexOf(d.getDay()) != -1){
39236                     // cell.title = ddaysText;
39237                    // cell.className = " fc-state-disabled";
39238                 }
39239             }
39240             if(ddMatch && format){
39241                 var fvalue = d.dateFormat(format);
39242                 if(ddMatch.test(fvalue)){
39243                     cell.title = ddText.replace("%0", fvalue);
39244                    cell.className = " fc-state-disabled";
39245                 }
39246             }
39247             
39248             if (!cell.initialClassName) {
39249                 cell.initialClassName = cell.dom.className;
39250             }
39251             
39252             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
39253         };
39254
39255         var i = 0;
39256         
39257         for(; i < startingPos; i++) {
39258             cells[i].dayName =  (++prevStart);
39259             Roo.log(textEls[i]);
39260             d.setDate(d.getDate()+1);
39261             
39262             //cells[i].className = "fc-past fc-other-month";
39263             setCellClass(this, cells[i]);
39264         }
39265         
39266         var intDay = 0;
39267         
39268         for(; i < days; i++){
39269             intDay = i - startingPos + 1;
39270             cells[i].dayName =  (intDay);
39271             d.setDate(d.getDate()+1);
39272             
39273             cells[i].className = ''; // "x-date-active";
39274             setCellClass(this, cells[i]);
39275         }
39276         var extraDays = 0;
39277         
39278         for(; i < 42; i++) {
39279             //textEls[i].innerHTML = (++extraDays);
39280             
39281             d.setDate(d.getDate()+1);
39282             cells[i].dayName = (++extraDays);
39283             cells[i].className = "fc-future fc-other-month";
39284             setCellClass(this, cells[i]);
39285         }
39286         
39287         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
39288         
39289         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
39290         
39291         // this will cause all the cells to mis
39292         var rows= [];
39293         var i =0;
39294         for (var r = 0;r < 6;r++) {
39295             for (var c =0;c < 7;c++) {
39296                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
39297             }    
39298         }
39299         
39300         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
39301         for(i=0;i<cells.length;i++) {
39302             
39303             this.cells.elements[i].dayName = cells[i].dayName ;
39304             this.cells.elements[i].className = cells[i].className;
39305             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
39306             this.cells.elements[i].title = cells[i].title ;
39307             this.cells.elements[i].dateValue = cells[i].dateValue ;
39308         }
39309         
39310         
39311         
39312         
39313         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
39314         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
39315         
39316         ////if(totalRows != 6){
39317             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
39318            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
39319        // }
39320         
39321         this.fireEvent('monthchange', this, date);
39322         
39323         
39324     },
39325  /**
39326      * Returns the grid's SelectionModel.
39327      * @return {SelectionModel}
39328      */
39329     getSelectionModel : function(){
39330         if(!this.selModel){
39331             this.selModel = new Roo.grid.CellSelectionModel();
39332         }
39333         return this.selModel;
39334     },
39335
39336     load: function() {
39337         this.eventStore.load()
39338         
39339         
39340         
39341     },
39342     
39343     findCell : function(dt) {
39344         dt = dt.clearTime().getTime();
39345         var ret = false;
39346         this.cells.each(function(c){
39347             //Roo.log("check " +c.dateValue + '?=' + dt);
39348             if(c.dateValue == dt){
39349                 ret = c;
39350                 return false;
39351             }
39352             return true;
39353         });
39354         
39355         return ret;
39356     },
39357     
39358     findCells : function(rec) {
39359         var s = rec.data.start_dt.clone().clearTime().getTime();
39360        // Roo.log(s);
39361         var e= rec.data.end_dt.clone().clearTime().getTime();
39362        // Roo.log(e);
39363         var ret = [];
39364         this.cells.each(function(c){
39365              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
39366             
39367             if(c.dateValue > e){
39368                 return ;
39369             }
39370             if(c.dateValue < s){
39371                 return ;
39372             }
39373             ret.push(c);
39374         });
39375         
39376         return ret;    
39377     },
39378     
39379     findBestRow: function(cells)
39380     {
39381         var ret = 0;
39382         
39383         for (var i =0 ; i < cells.length;i++) {
39384             ret  = Math.max(cells[i].rows || 0,ret);
39385         }
39386         return ret;
39387         
39388     },
39389     
39390     
39391     addItem : function(rec)
39392     {
39393         // look for vertical location slot in
39394         var cells = this.findCells(rec);
39395         
39396         rec.row = this.findBestRow(cells);
39397         
39398         // work out the location.
39399         
39400         var crow = false;
39401         var rows = [];
39402         for(var i =0; i < cells.length; i++) {
39403             if (!crow) {
39404                 crow = {
39405                     start : cells[i],
39406                     end :  cells[i]
39407                 };
39408                 continue;
39409             }
39410             if (crow.start.getY() == cells[i].getY()) {
39411                 // on same row.
39412                 crow.end = cells[i];
39413                 continue;
39414             }
39415             // different row.
39416             rows.push(crow);
39417             crow = {
39418                 start: cells[i],
39419                 end : cells[i]
39420             };
39421             
39422         }
39423         
39424         rows.push(crow);
39425         rec.els = [];
39426         rec.rows = rows;
39427         rec.cells = cells;
39428         for (var i = 0; i < cells.length;i++) {
39429             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
39430             
39431         }
39432         
39433         
39434     },
39435     
39436     clearEvents: function() {
39437         
39438         if (!this.eventStore.getCount()) {
39439             return;
39440         }
39441         // reset number of rows in cells.
39442         Roo.each(this.cells.elements, function(c){
39443             c.rows = 0;
39444         });
39445         
39446         this.eventStore.each(function(e) {
39447             this.clearEvent(e);
39448         },this);
39449         
39450     },
39451     
39452     clearEvent : function(ev)
39453     {
39454         if (ev.els) {
39455             Roo.each(ev.els, function(el) {
39456                 el.un('mouseenter' ,this.onEventEnter, this);
39457                 el.un('mouseleave' ,this.onEventLeave, this);
39458                 el.remove();
39459             },this);
39460             ev.els = [];
39461         }
39462     },
39463     
39464     
39465     renderEvent : function(ev,ctr) {
39466         if (!ctr) {
39467              ctr = this.view.el.select('.fc-event-container',true).first();
39468         }
39469         
39470          
39471         this.clearEvent(ev);
39472             //code
39473        
39474         
39475         
39476         ev.els = [];
39477         var cells = ev.cells;
39478         var rows = ev.rows;
39479         this.fireEvent('eventrender', this, ev);
39480         
39481         for(var i =0; i < rows.length; i++) {
39482             
39483             cls = '';
39484             if (i == 0) {
39485                 cls += ' fc-event-start';
39486             }
39487             if ((i+1) == rows.length) {
39488                 cls += ' fc-event-end';
39489             }
39490             
39491             //Roo.log(ev.data);
39492             // how many rows should it span..
39493             var cg = this.eventTmpl.append(ctr,Roo.apply({
39494                 fccls : cls
39495                 
39496             }, ev.data) , true);
39497             
39498             
39499             cg.on('mouseenter' ,this.onEventEnter, this, ev);
39500             cg.on('mouseleave' ,this.onEventLeave, this, ev);
39501             cg.on('click', this.onEventClick, this, ev);
39502             
39503             ev.els.push(cg);
39504             
39505             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
39506             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
39507             //Roo.log(cg);
39508              
39509             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
39510             cg.setWidth(ebox.right - sbox.x -2);
39511         }
39512     },
39513     
39514     renderEvents: function()
39515     {   
39516         // first make sure there is enough space..
39517         
39518         if (!this.eventTmpl) {
39519             this.eventTmpl = new Roo.Template(
39520                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
39521                     '<div class="fc-event-inner">' +
39522                         '<span class="fc-event-time">{time}</span>' +
39523                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
39524                     '</div>' +
39525                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
39526                 '</div>'
39527             );
39528                 
39529         }
39530                
39531         
39532         
39533         this.cells.each(function(c) {
39534             //Roo.log(c.select('.fc-day-content div',true).first());
39535             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
39536         });
39537         
39538         var ctr = this.view.el.select('.fc-event-container',true).first();
39539         
39540         var cls;
39541         this.eventStore.each(function(ev){
39542             
39543             this.renderEvent(ev);
39544              
39545              
39546         }, this);
39547         this.view.layout();
39548         
39549     },
39550     
39551     onEventEnter: function (e, el,event,d) {
39552         this.fireEvent('evententer', this, el, event);
39553     },
39554     
39555     onEventLeave: function (e, el,event,d) {
39556         this.fireEvent('eventleave', this, el, event);
39557     },
39558     
39559     onEventClick: function (e, el,event,d) {
39560         this.fireEvent('eventclick', this, el, event);
39561     },
39562     
39563     onMonthChange: function () {
39564         this.store.load();
39565     },
39566     
39567     onLoad: function () {
39568         
39569         //Roo.log('calendar onload');
39570 //         
39571         if(this.eventStore.getCount() > 0){
39572             
39573            
39574             
39575             this.eventStore.each(function(d){
39576                 
39577                 
39578                 // FIXME..
39579                 var add =   d.data;
39580                 if (typeof(add.end_dt) == 'undefined')  {
39581                     Roo.log("Missing End time in calendar data: ");
39582                     Roo.log(d);
39583                     return;
39584                 }
39585                 if (typeof(add.start_dt) == 'undefined')  {
39586                     Roo.log("Missing Start time in calendar data: ");
39587                     Roo.log(d);
39588                     return;
39589                 }
39590                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
39591                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
39592                 add.id = add.id || d.id;
39593                 add.title = add.title || '??';
39594                 
39595                 this.addItem(d);
39596                 
39597              
39598             },this);
39599         }
39600         
39601         this.renderEvents();
39602     }
39603     
39604
39605 });
39606 /*
39607  grid : {
39608                 xtype: 'Grid',
39609                 xns: Roo.grid,
39610                 listeners : {
39611                     render : function ()
39612                     {
39613                         _this.grid = this;
39614                         
39615                         if (!this.view.el.hasClass('course-timesheet')) {
39616                             this.view.el.addClass('course-timesheet');
39617                         }
39618                         if (this.tsStyle) {
39619                             this.ds.load({});
39620                             return; 
39621                         }
39622                         Roo.log('width');
39623                         Roo.log(_this.grid.view.el.getWidth());
39624                         
39625                         
39626                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
39627                             '.course-timesheet .x-grid-row' : {
39628                                 height: '80px'
39629                             },
39630                             '.x-grid-row td' : {
39631                                 'vertical-align' : 0
39632                             },
39633                             '.course-edit-link' : {
39634                                 'color' : 'blue',
39635                                 'text-overflow' : 'ellipsis',
39636                                 'overflow' : 'hidden',
39637                                 'white-space' : 'nowrap',
39638                                 'cursor' : 'pointer'
39639                             },
39640                             '.sub-link' : {
39641                                 'color' : 'green'
39642                             },
39643                             '.de-act-sup-link' : {
39644                                 'color' : 'purple',
39645                                 'text-decoration' : 'line-through'
39646                             },
39647                             '.de-act-link' : {
39648                                 'color' : 'red',
39649                                 'text-decoration' : 'line-through'
39650                             },
39651                             '.course-timesheet .course-highlight' : {
39652                                 'border-top-style': 'dashed !important',
39653                                 'border-bottom-bottom': 'dashed !important'
39654                             },
39655                             '.course-timesheet .course-item' : {
39656                                 'font-family'   : 'tahoma, arial, helvetica',
39657                                 'font-size'     : '11px',
39658                                 'overflow'      : 'hidden',
39659                                 'padding-left'  : '10px',
39660                                 'padding-right' : '10px',
39661                                 'padding-top' : '10px' 
39662                             }
39663                             
39664                         }, Roo.id());
39665                                 this.ds.load({});
39666                     }
39667                 },
39668                 autoWidth : true,
39669                 monitorWindowResize : false,
39670                 cellrenderer : function(v,x,r)
39671                 {
39672                     return v;
39673                 },
39674                 sm : {
39675                     xtype: 'CellSelectionModel',
39676                     xns: Roo.grid
39677                 },
39678                 dataSource : {
39679                     xtype: 'Store',
39680                     xns: Roo.data,
39681                     listeners : {
39682                         beforeload : function (_self, options)
39683                         {
39684                             options.params = options.params || {};
39685                             options.params._month = _this.monthField.getValue();
39686                             options.params.limit = 9999;
39687                             options.params['sort'] = 'when_dt';    
39688                             options.params['dir'] = 'ASC';    
39689                             this.proxy.loadResponse = this.loadResponse;
39690                             Roo.log("load?");
39691                             //this.addColumns();
39692                         },
39693                         load : function (_self, records, options)
39694                         {
39695                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
39696                                 // if you click on the translation.. you can edit it...
39697                                 var el = Roo.get(this);
39698                                 var id = el.dom.getAttribute('data-id');
39699                                 var d = el.dom.getAttribute('data-date');
39700                                 var t = el.dom.getAttribute('data-time');
39701                                 //var id = this.child('span').dom.textContent;
39702                                 
39703                                 //Roo.log(this);
39704                                 Pman.Dialog.CourseCalendar.show({
39705                                     id : id,
39706                                     when_d : d,
39707                                     when_t : t,
39708                                     productitem_active : id ? 1 : 0
39709                                 }, function() {
39710                                     _this.grid.ds.load({});
39711                                 });
39712                            
39713                            });
39714                            
39715                            _this.panel.fireEvent('resize', [ '', '' ]);
39716                         }
39717                     },
39718                     loadResponse : function(o, success, response){
39719                             // this is overridden on before load..
39720                             
39721                             Roo.log("our code?");       
39722                             //Roo.log(success);
39723                             //Roo.log(response)
39724                             delete this.activeRequest;
39725                             if(!success){
39726                                 this.fireEvent("loadexception", this, o, response);
39727                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
39728                                 return;
39729                             }
39730                             var result;
39731                             try {
39732                                 result = o.reader.read(response);
39733                             }catch(e){
39734                                 Roo.log("load exception?");
39735                                 this.fireEvent("loadexception", this, o, response, e);
39736                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
39737                                 return;
39738                             }
39739                             Roo.log("ready...");        
39740                             // loop through result.records;
39741                             // and set this.tdate[date] = [] << array of records..
39742                             _this.tdata  = {};
39743                             Roo.each(result.records, function(r){
39744                                 //Roo.log(r.data);
39745                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
39746                                     _this.tdata[r.data.when_dt.format('j')] = [];
39747                                 }
39748                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
39749                             });
39750                             
39751                             //Roo.log(_this.tdata);
39752                             
39753                             result.records = [];
39754                             result.totalRecords = 6;
39755                     
39756                             // let's generate some duumy records for the rows.
39757                             //var st = _this.dateField.getValue();
39758                             
39759                             // work out monday..
39760                             //st = st.add(Date.DAY, -1 * st.format('w'));
39761                             
39762                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
39763                             
39764                             var firstOfMonth = date.getFirstDayOfMonth();
39765                             var days = date.getDaysInMonth();
39766                             var d = 1;
39767                             var firstAdded = false;
39768                             for (var i = 0; i < result.totalRecords ; i++) {
39769                                 //var d= st.add(Date.DAY, i);
39770                                 var row = {};
39771                                 var added = 0;
39772                                 for(var w = 0 ; w < 7 ; w++){
39773                                     if(!firstAdded && firstOfMonth != w){
39774                                         continue;
39775                                     }
39776                                     if(d > days){
39777                                         continue;
39778                                     }
39779                                     firstAdded = true;
39780                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
39781                                     row['weekday'+w] = String.format(
39782                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
39783                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
39784                                                     d,
39785                                                     date.format('Y-m-')+dd
39786                                                 );
39787                                     added++;
39788                                     if(typeof(_this.tdata[d]) != 'undefined'){
39789                                         Roo.each(_this.tdata[d], function(r){
39790                                             var is_sub = '';
39791                                             var deactive = '';
39792                                             var id = r.id;
39793                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
39794                                             if(r.parent_id*1>0){
39795                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
39796                                                 id = r.parent_id;
39797                                             }
39798                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
39799                                                 deactive = 'de-act-link';
39800                                             }
39801                                             
39802                                             row['weekday'+w] += String.format(
39803                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
39804                                                     id, //0
39805                                                     r.product_id_name, //1
39806                                                     r.when_dt.format('h:ia'), //2
39807                                                     is_sub, //3
39808                                                     deactive, //4
39809                                                     desc // 5
39810                                             );
39811                                         });
39812                                     }
39813                                     d++;
39814                                 }
39815                                 
39816                                 // only do this if something added..
39817                                 if(added > 0){ 
39818                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
39819                                 }
39820                                 
39821                                 
39822                                 // push it twice. (second one with an hour..
39823                                 
39824                             }
39825                             //Roo.log(result);
39826                             this.fireEvent("load", this, o, o.request.arg);
39827                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
39828                         },
39829                     sortInfo : {field: 'when_dt', direction : 'ASC' },
39830                     proxy : {
39831                         xtype: 'HttpProxy',
39832                         xns: Roo.data,
39833                         method : 'GET',
39834                         url : baseURL + '/Roo/Shop_course.php'
39835                     },
39836                     reader : {
39837                         xtype: 'JsonReader',
39838                         xns: Roo.data,
39839                         id : 'id',
39840                         fields : [
39841                             {
39842                                 'name': 'id',
39843                                 'type': 'int'
39844                             },
39845                             {
39846                                 'name': 'when_dt',
39847                                 'type': 'string'
39848                             },
39849                             {
39850                                 'name': 'end_dt',
39851                                 'type': 'string'
39852                             },
39853                             {
39854                                 'name': 'parent_id',
39855                                 'type': 'int'
39856                             },
39857                             {
39858                                 'name': 'product_id',
39859                                 'type': 'int'
39860                             },
39861                             {
39862                                 'name': 'productitem_id',
39863                                 'type': 'int'
39864                             },
39865                             {
39866                                 'name': 'guid',
39867                                 'type': 'int'
39868                             }
39869                         ]
39870                     }
39871                 },
39872                 toolbar : {
39873                     xtype: 'Toolbar',
39874                     xns: Roo,
39875                     items : [
39876                         {
39877                             xtype: 'Button',
39878                             xns: Roo.Toolbar,
39879                             listeners : {
39880                                 click : function (_self, e)
39881                                 {
39882                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
39883                                     sd.setMonth(sd.getMonth()-1);
39884                                     _this.monthField.setValue(sd.format('Y-m-d'));
39885                                     _this.grid.ds.load({});
39886                                 }
39887                             },
39888                             text : "Back"
39889                         },
39890                         {
39891                             xtype: 'Separator',
39892                             xns: Roo.Toolbar
39893                         },
39894                         {
39895                             xtype: 'MonthField',
39896                             xns: Roo.form,
39897                             listeners : {
39898                                 render : function (_self)
39899                                 {
39900                                     _this.monthField = _self;
39901                                    // _this.monthField.set  today
39902                                 },
39903                                 select : function (combo, date)
39904                                 {
39905                                     _this.grid.ds.load({});
39906                                 }
39907                             },
39908                             value : (function() { return new Date(); })()
39909                         },
39910                         {
39911                             xtype: 'Separator',
39912                             xns: Roo.Toolbar
39913                         },
39914                         {
39915                             xtype: 'TextItem',
39916                             xns: Roo.Toolbar,
39917                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
39918                         },
39919                         {
39920                             xtype: 'Fill',
39921                             xns: Roo.Toolbar
39922                         },
39923                         {
39924                             xtype: 'Button',
39925                             xns: Roo.Toolbar,
39926                             listeners : {
39927                                 click : function (_self, e)
39928                                 {
39929                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
39930                                     sd.setMonth(sd.getMonth()+1);
39931                                     _this.monthField.setValue(sd.format('Y-m-d'));
39932                                     _this.grid.ds.load({});
39933                                 }
39934                             },
39935                             text : "Next"
39936                         }
39937                     ]
39938                 },
39939                  
39940             }
39941         };
39942         
39943         *//*
39944  * Based on:
39945  * Ext JS Library 1.1.1
39946  * Copyright(c) 2006-2007, Ext JS, LLC.
39947  *
39948  * Originally Released Under LGPL - original licence link has changed is not relivant.
39949  *
39950  * Fork - LGPL
39951  * <script type="text/javascript">
39952  */
39953  
39954 /**
39955  * @class Roo.LoadMask
39956  * A simple utility class for generically masking elements while loading data.  If the element being masked has
39957  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
39958  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
39959  * element's UpdateManager load indicator and will be destroyed after the initial load.
39960  * @constructor
39961  * Create a new LoadMask
39962  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
39963  * @param {Object} config The config object
39964  */
39965 Roo.LoadMask = function(el, config){
39966     this.el = Roo.get(el);
39967     Roo.apply(this, config);
39968     if(this.store){
39969         this.store.on('beforeload', this.onBeforeLoad, this);
39970         this.store.on('load', this.onLoad, this);
39971         this.store.on('loadexception', this.onLoadException, this);
39972         this.removeMask = false;
39973     }else{
39974         var um = this.el.getUpdateManager();
39975         um.showLoadIndicator = false; // disable the default indicator
39976         um.on('beforeupdate', this.onBeforeLoad, this);
39977         um.on('update', this.onLoad, this);
39978         um.on('failure', this.onLoad, this);
39979         this.removeMask = true;
39980     }
39981 };
39982
39983 Roo.LoadMask.prototype = {
39984     /**
39985      * @cfg {Boolean} removeMask
39986      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
39987      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
39988      */
39989     removeMask : false,
39990     /**
39991      * @cfg {String} msg
39992      * The text to display in a centered loading message box (defaults to 'Loading...')
39993      */
39994     msg : 'Loading...',
39995     /**
39996      * @cfg {String} msgCls
39997      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
39998      */
39999     msgCls : 'x-mask-loading',
40000
40001     /**
40002      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
40003      * @type Boolean
40004      */
40005     disabled: false,
40006
40007     /**
40008      * Disables the mask to prevent it from being displayed
40009      */
40010     disable : function(){
40011        this.disabled = true;
40012     },
40013
40014     /**
40015      * Enables the mask so that it can be displayed
40016      */
40017     enable : function(){
40018         this.disabled = false;
40019     },
40020     
40021     onLoadException : function()
40022     {
40023         Roo.log(arguments);
40024         
40025         if (typeof(arguments[3]) != 'undefined') {
40026             Roo.MessageBox.alert("Error loading",arguments[3]);
40027         } 
40028         /*
40029         try {
40030             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
40031                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
40032             }   
40033         } catch(e) {
40034             
40035         }
40036         */
40037     
40038         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
40039     },
40040     // private
40041     onLoad : function()
40042     {
40043         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
40044     },
40045
40046     // private
40047     onBeforeLoad : function(){
40048         if(!this.disabled){
40049             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
40050         }
40051     },
40052
40053     // private
40054     destroy : function(){
40055         if(this.store){
40056             this.store.un('beforeload', this.onBeforeLoad, this);
40057             this.store.un('load', this.onLoad, this);
40058             this.store.un('loadexception', this.onLoadException, this);
40059         }else{
40060             var um = this.el.getUpdateManager();
40061             um.un('beforeupdate', this.onBeforeLoad, this);
40062             um.un('update', this.onLoad, this);
40063             um.un('failure', this.onLoad, this);
40064         }
40065     }
40066 };/*
40067  * Based on:
40068  * Ext JS Library 1.1.1
40069  * Copyright(c) 2006-2007, Ext JS, LLC.
40070  *
40071  * Originally Released Under LGPL - original licence link has changed is not relivant.
40072  *
40073  * Fork - LGPL
40074  * <script type="text/javascript">
40075  */
40076
40077
40078 /**
40079  * @class Roo.XTemplate
40080  * @extends Roo.Template
40081  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
40082 <pre><code>
40083 var t = new Roo.XTemplate(
40084         '&lt;select name="{name}"&gt;',
40085                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
40086         '&lt;/select&gt;'
40087 );
40088  
40089 // then append, applying the master template values
40090  </code></pre>
40091  *
40092  * Supported features:
40093  *
40094  *  Tags:
40095
40096 <pre><code>
40097       {a_variable} - output encoded.
40098       {a_variable.format:("Y-m-d")} - call a method on the variable
40099       {a_variable:raw} - unencoded output
40100       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
40101       {a_variable:this.method_on_template(...)} - call a method on the template object.
40102  
40103 </code></pre>
40104  *  The tpl tag:
40105 <pre><code>
40106         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
40107         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
40108         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
40109         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
40110   
40111         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
40112         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
40113 </code></pre>
40114  *      
40115  */
40116 Roo.XTemplate = function()
40117 {
40118     Roo.XTemplate.superclass.constructor.apply(this, arguments);
40119     if (this.html) {
40120         this.compile();
40121     }
40122 };
40123
40124
40125 Roo.extend(Roo.XTemplate, Roo.Template, {
40126
40127     /**
40128      * The various sub templates
40129      */
40130     tpls : false,
40131     /**
40132      *
40133      * basic tag replacing syntax
40134      * WORD:WORD()
40135      *
40136      * // you can fake an object call by doing this
40137      *  x.t:(test,tesT) 
40138      * 
40139      */
40140     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
40141
40142     /**
40143      * compile the template
40144      *
40145      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
40146      *
40147      */
40148     compile: function()
40149     {
40150         var s = this.html;
40151      
40152         s = ['<tpl>', s, '</tpl>'].join('');
40153     
40154         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
40155             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
40156             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
40157             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
40158             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
40159             m,
40160             id     = 0,
40161             tpls   = [];
40162     
40163         while(true == !!(m = s.match(re))){
40164             var forMatch   = m[0].match(nameRe),
40165                 ifMatch   = m[0].match(ifRe),
40166                 execMatch   = m[0].match(execRe),
40167                 namedMatch   = m[0].match(namedRe),
40168                 
40169                 exp  = null, 
40170                 fn   = null,
40171                 exec = null,
40172                 name = forMatch && forMatch[1] ? forMatch[1] : '';
40173                 
40174             if (ifMatch) {
40175                 // if - puts fn into test..
40176                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
40177                 if(exp){
40178                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
40179                 }
40180             }
40181             
40182             if (execMatch) {
40183                 // exec - calls a function... returns empty if true is  returned.
40184                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
40185                 if(exp){
40186                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
40187                 }
40188             }
40189             
40190             
40191             if (name) {
40192                 // for = 
40193                 switch(name){
40194                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
40195                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
40196                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
40197                 }
40198             }
40199             var uid = namedMatch ? namedMatch[1] : id;
40200             
40201             
40202             tpls.push({
40203                 id:     namedMatch ? namedMatch[1] : id,
40204                 target: name,
40205                 exec:   exec,
40206                 test:   fn,
40207                 body:   m[1] || ''
40208             });
40209             if (namedMatch) {
40210                 s = s.replace(m[0], '');
40211             } else { 
40212                 s = s.replace(m[0], '{xtpl'+ id + '}');
40213             }
40214             ++id;
40215         }
40216         this.tpls = [];
40217         for(var i = tpls.length-1; i >= 0; --i){
40218             this.compileTpl(tpls[i]);
40219             this.tpls[tpls[i].id] = tpls[i];
40220         }
40221         this.master = tpls[tpls.length-1];
40222         return this;
40223     },
40224     /**
40225      * same as applyTemplate, except it's done to one of the subTemplates
40226      * when using named templates, you can do:
40227      *
40228      * var str = pl.applySubTemplate('your-name', values);
40229      *
40230      * 
40231      * @param {Number} id of the template
40232      * @param {Object} values to apply to template
40233      * @param {Object} parent (normaly the instance of this object)
40234      */
40235     applySubTemplate : function(id, values, parent)
40236     {
40237         
40238         
40239         var t = this.tpls[id];
40240         
40241         
40242         try { 
40243             if(t.test && !t.test.call(this, values, parent)){
40244                 return '';
40245             }
40246         } catch(e) {
40247             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
40248             Roo.log(e.toString());
40249             Roo.log(t.test);
40250             return ''
40251         }
40252         try { 
40253             
40254             if(t.exec && t.exec.call(this, values, parent)){
40255                 return '';
40256             }
40257         } catch(e) {
40258             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
40259             Roo.log(e.toString());
40260             Roo.log(t.exec);
40261             return ''
40262         }
40263         try {
40264             var vs = t.target ? t.target.call(this, values, parent) : values;
40265             parent = t.target ? values : parent;
40266             if(t.target && vs instanceof Array){
40267                 var buf = [];
40268                 for(var i = 0, len = vs.length; i < len; i++){
40269                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
40270                 }
40271                 return buf.join('');
40272             }
40273             return t.compiled.call(this, vs, parent);
40274         } catch (e) {
40275             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
40276             Roo.log(e.toString());
40277             Roo.log(t.compiled);
40278             return '';
40279         }
40280     },
40281
40282     compileTpl : function(tpl)
40283     {
40284         var fm = Roo.util.Format;
40285         var useF = this.disableFormats !== true;
40286         var sep = Roo.isGecko ? "+" : ",";
40287         var undef = function(str) {
40288             Roo.log("Property not found :"  + str);
40289             return '';
40290         };
40291         
40292         var fn = function(m, name, format, args)
40293         {
40294             //Roo.log(arguments);
40295             args = args ? args.replace(/\\'/g,"'") : args;
40296             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
40297             if (typeof(format) == 'undefined') {
40298                 format= 'htmlEncode';
40299             }
40300             if (format == 'raw' ) {
40301                 format = false;
40302             }
40303             
40304             if(name.substr(0, 4) == 'xtpl'){
40305                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
40306             }
40307             
40308             // build an array of options to determine if value is undefined..
40309             
40310             // basically get 'xxxx.yyyy' then do
40311             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
40312             //    (function () { Roo.log("Property not found"); return ''; })() :
40313             //    ......
40314             
40315             var udef_ar = [];
40316             var lookfor = '';
40317             Roo.each(name.split('.'), function(st) {
40318                 lookfor += (lookfor.length ? '.': '') + st;
40319                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
40320             });
40321             
40322             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
40323             
40324             
40325             if(format && useF){
40326                 
40327                 args = args ? ',' + args : "";
40328                  
40329                 if(format.substr(0, 5) != "this."){
40330                     format = "fm." + format + '(';
40331                 }else{
40332                     format = 'this.call("'+ format.substr(5) + '", ';
40333                     args = ", values";
40334                 }
40335                 
40336                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
40337             }
40338              
40339             if (args.length) {
40340                 // called with xxyx.yuu:(test,test)
40341                 // change to ()
40342                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
40343             }
40344             // raw.. - :raw modifier..
40345             return "'"+ sep + udef_st  + name + ")"+sep+"'";
40346             
40347         };
40348         var body;
40349         // branched to use + in gecko and [].join() in others
40350         if(Roo.isGecko){
40351             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
40352                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
40353                     "';};};";
40354         }else{
40355             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
40356             body.push(tpl.body.replace(/(\r\n|\n)/g,
40357                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
40358             body.push("'].join('');};};");
40359             body = body.join('');
40360         }
40361         
40362         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
40363        
40364         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
40365         eval(body);
40366         
40367         return this;
40368     },
40369
40370     applyTemplate : function(values){
40371         return this.master.compiled.call(this, values, {});
40372         //var s = this.subs;
40373     },
40374
40375     apply : function(){
40376         return this.applyTemplate.apply(this, arguments);
40377     }
40378
40379  });
40380
40381 Roo.XTemplate.from = function(el){
40382     el = Roo.getDom(el);
40383     return new Roo.XTemplate(el.value || el.innerHTML);
40384 };