5291b8ce7416f1f8b34f6dcf1ff57185dfbd4367
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @singleton
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570  
571         if(this.pruneModifiedRecords){
572             this.modified.remove(record);
573         }
574         this.fireEvent("remove", this, record, index);
575     },
576
577     /**
578      * Remove all Records from the Store and fires the clear event.
579      */
580     removeAll : function(){
581         this.data.clear();
582         if(this.pruneModifiedRecords){
583             this.modified = [];
584         }
585         this.fireEvent("clear", this);
586     },
587
588     /**
589      * Inserts Records to the Store at the given index and fires the add event.
590      * @param {Number} index The start index at which to insert the passed Records.
591      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592      */
593     insert : function(index, records){
594         records = [].concat(records);
595         for(var i = 0, len = records.length; i < len; i++){
596             this.data.insert(index, records[i]);
597             records[i].join(this);
598         }
599         this.fireEvent("add", this, records, index);
600     },
601
602     /**
603      * Get the index within the cache of the passed Record.
604      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605      * @return {Number} The index of the passed Record. Returns -1 if not found.
606      */
607     indexOf : function(record){
608         return this.data.indexOf(record);
609     },
610
611     /**
612      * Get the index within the cache of the Record with the passed id.
613      * @param {String} id The id of the Record to find.
614      * @return {Number} The index of the Record. Returns -1 if not found.
615      */
616     indexOfId : function(id){
617         return this.data.indexOfKey(id);
618     },
619
620     /**
621      * Get the Record with the specified id.
622      * @param {String} id The id of the Record to find.
623      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624      */
625     getById : function(id){
626         return this.data.key(id);
627     },
628
629     /**
630      * Get the Record at the specified index.
631      * @param {Number} index The index of the Record to find.
632      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633      */
634     getAt : function(index){
635         return this.data.itemAt(index);
636     },
637
638     /**
639      * Returns a range of Records between specified indices.
640      * @param {Number} startIndex (optional) The starting index (defaults to 0)
641      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642      * @return {Roo.data.Record[]} An array of Records
643      */
644     getRange : function(start, end){
645         return this.data.getRange(start, end);
646     },
647
648     // private
649     storeOptions : function(o){
650         o = Roo.apply({}, o);
651         delete o.callback;
652         delete o.scope;
653         this.lastOptions = o;
654     },
655
656     /**
657      * Loads the Record cache from the configured Proxy using the configured Reader.
658      * <p>
659      * If using remote paging, then the first load call must specify the <em>start</em>
660      * and <em>limit</em> properties in the options.params property to establish the initial
661      * position within the dataset, and the number of Records to cache on each read from the Proxy.
662      * <p>
663      * <strong>It is important to note that for remote data sources, loading is asynchronous,
664      * and this call will return before the new data has been loaded. Perform any post-processing
665      * in a callback function, or in a "load" event handler.</strong>
666      * <p>
667      * @param {Object} options An object containing properties which control loading options:<ul>
668      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
670      * passed the following arguments:<ul>
671      * <li>r : Roo.data.Record[]</li>
672      * <li>options: Options object from the load call</li>
673      * <li>success: Boolean success indicator</li></ul></li>
674      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
675      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
676      * </ul>
677      */
678     load : function(options){
679         options = options || {};
680         if(this.fireEvent("beforeload", this, options) !== false){
681             this.storeOptions(options);
682             var p = Roo.apply(options.params || {}, this.baseParams);
683             // if meta was not loaded from remote source.. try requesting it.
684             if (!this.reader.metaFromRemote) {
685                 p._requestMeta = 1;
686             }
687             if(this.sortInfo && this.remoteSort){
688                 var pn = this.paramNames;
689                 p[pn["sort"]] = this.sortInfo.field;
690                 p[pn["dir"]] = this.sortInfo.direction;
691             }
692             if (this.multiSort) {
693                 var pn = this.paramNames;
694                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
695             }
696             
697             this.proxy.load(p, this.reader, this.loadRecords, this, options);
698         }
699     },
700
701     /**
702      * Reloads the Record cache from the configured Proxy using the configured Reader and
703      * the options from the last load operation performed.
704      * @param {Object} options (optional) An object containing properties which may override the options
705      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
706      * the most recently used options are reused).
707      */
708     reload : function(options){
709         this.load(Roo.applyIf(options||{}, this.lastOptions));
710     },
711
712     // private
713     // Called as a callback by the Reader during a load operation.
714     loadRecords : function(o, options, success){
715         if(!o || success === false){
716             if(success !== false){
717                 this.fireEvent("load", this, [], options, o);
718             }
719             if(options.callback){
720                 options.callback.call(options.scope || this, [], options, false);
721             }
722             return;
723         }
724         // if data returned failure - throw an exception.
725         if (o.success === false) {
726             // show a message if no listener is registered.
727             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
728                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
729             }
730             // loadmask wil be hooked into this..
731             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
732             return;
733         }
734         var r = o.records, t = o.totalRecords || r.length;
735         
736         this.fireEvent("beforeloadadd", this, r, options, o);
737         
738         if(!options || options.add !== true){
739             if(this.pruneModifiedRecords){
740                 this.modified = [];
741             }
742             for(var i = 0, len = r.length; i < len; i++){
743                 r[i].join(this);
744             }
745             if(this.snapshot){
746                 this.data = this.snapshot;
747                 delete this.snapshot;
748             }
749             this.data.clear();
750             this.data.addAll(r);
751             this.totalLength = t;
752             this.applySort();
753             this.fireEvent("datachanged", this);
754         }else{
755             this.totalLength = Math.max(t, this.data.length+r.length);
756             this.add(r);
757         }
758         
759         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
760                 
761             var e = new Roo.data.Record({});
762
763             e.set(this.parent.displayField, this.parent.emptyTitle);
764             e.set(this.parent.valueField, '');
765
766             this.insert(0, e);
767         }
768             
769         this.fireEvent("load", this, r, options, o);
770         if(options.callback){
771             options.callback.call(options.scope || this, r, options, true);
772         }
773     },
774
775
776     /**
777      * Loads data from a passed data block. A Reader which understands the format of the data
778      * must have been configured in the constructor.
779      * @param {Object} data The data block from which to read the Records.  The format of the data expected
780      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
781      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
782      */
783     loadData : function(o, append){
784         var r = this.reader.readRecords(o);
785         this.loadRecords(r, {add: append}, true);
786     },
787
788     /**
789      * Gets the number of cached records.
790      * <p>
791      * <em>If using paging, this may not be the total size of the dataset. If the data object
792      * used by the Reader contains the dataset size, then the getTotalCount() function returns
793      * the data set size</em>
794      */
795     getCount : function(){
796         return this.data.length || 0;
797     },
798
799     /**
800      * Gets the total number of records in the dataset as returned by the server.
801      * <p>
802      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
803      * the dataset size</em>
804      */
805     getTotalCount : function(){
806         return this.totalLength || 0;
807     },
808
809     /**
810      * Returns the sort state of the Store as an object with two properties:
811      * <pre><code>
812  field {String} The name of the field by which the Records are sorted
813  direction {String} The sort order, "ASC" or "DESC"
814      * </code></pre>
815      */
816     getSortState : function(){
817         return this.sortInfo;
818     },
819
820     // private
821     applySort : function(){
822         if(this.sortInfo && !this.remoteSort){
823             var s = this.sortInfo, f = s.field;
824             var st = this.fields.get(f).sortType;
825             var fn = function(r1, r2){
826                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
827                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
828             };
829             this.data.sort(s.direction, fn);
830             if(this.snapshot && this.snapshot != this.data){
831                 this.snapshot.sort(s.direction, fn);
832             }
833         }
834     },
835
836     /**
837      * Sets the default sort column and order to be used by the next load operation.
838      * @param {String} fieldName The name of the field to sort by.
839      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
840      */
841     setDefaultSort : function(field, dir){
842         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
843     },
844
845     /**
846      * Sort the Records.
847      * If remote sorting is used, the sort is performed on the server, and the cache is
848      * reloaded. If local sorting is used, the cache is sorted internally.
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     sort : function(fieldName, dir){
853         var f = this.fields.get(fieldName);
854         if(!dir){
855             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
856             
857             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
858                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
859             }else{
860                 dir = f.sortDir;
861             }
862         }
863         this.sortToggle[f.name] = dir;
864         this.sortInfo = {field: f.name, direction: dir};
865         if(!this.remoteSort){
866             this.applySort();
867             this.fireEvent("datachanged", this);
868         }else{
869             this.load(this.lastOptions);
870         }
871     },
872
873     /**
874      * Calls the specified function for each of the Records in the cache.
875      * @param {Function} fn The function to call. The Record is passed as the first parameter.
876      * Returning <em>false</em> aborts and exits the iteration.
877      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
878      */
879     each : function(fn, scope){
880         this.data.each(fn, scope);
881     },
882
883     /**
884      * Gets all records modified since the last commit.  Modified records are persisted across load operations
885      * (e.g., during paging).
886      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
887      */
888     getModifiedRecords : function(){
889         return this.modified;
890     },
891
892     // private
893     createFilterFn : function(property, value, anyMatch){
894         if(!value.exec){ // not a regex
895             value = String(value);
896             if(value.length == 0){
897                 return false;
898             }
899             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
900         }
901         return function(r){
902             return value.test(r.data[property]);
903         };
904     },
905
906     /**
907      * Sums the value of <i>property</i> for each record between start and end and returns the result.
908      * @param {String} property A field on your records
909      * @param {Number} start The record index to start at (defaults to 0)
910      * @param {Number} end The last record index to include (defaults to length - 1)
911      * @return {Number} The sum
912      */
913     sum : function(property, start, end){
914         var rs = this.data.items, v = 0;
915         start = start || 0;
916         end = (end || end === 0) ? end : rs.length-1;
917
918         for(var i = start; i <= end; i++){
919             v += (rs[i].data[property] || 0);
920         }
921         return v;
922     },
923
924     /**
925      * Filter the records by a specified property.
926      * @param {String} field A field on your records
927      * @param {String/RegExp} value Either a string that the field
928      * should start with or a RegExp to test against the field
929      * @param {Boolean} anyMatch True to match any part not just the beginning
930      */
931     filter : function(property, value, anyMatch){
932         var fn = this.createFilterFn(property, value, anyMatch);
933         return fn ? this.filterBy(fn) : this.clearFilter();
934     },
935
936     /**
937      * Filter by a function. The specified function will be called with each
938      * record in this data source. If the function returns true the record is included,
939      * otherwise it is filtered.
940      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
941      * @param {Object} scope (optional) The scope of the function (defaults to this)
942      */
943     filterBy : function(fn, scope){
944         this.snapshot = this.snapshot || this.data;
945         this.data = this.queryBy(fn, scope||this);
946         this.fireEvent("datachanged", this);
947     },
948
949     /**
950      * Query the records by a specified property.
951      * @param {String} field A field on your records
952      * @param {String/RegExp} value Either a string that the field
953      * should start with or a RegExp to test against the field
954      * @param {Boolean} anyMatch True to match any part not just the beginning
955      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
956      */
957     query : function(property, value, anyMatch){
958         var fn = this.createFilterFn(property, value, anyMatch);
959         return fn ? this.queryBy(fn) : this.data.clone();
960     },
961
962     /**
963      * Query by a function. The specified function will be called with each
964      * record in this data source. If the function returns true the record is included
965      * in the results.
966      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
967      * @param {Object} scope (optional) The scope of the function (defaults to this)
968       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
969      **/
970     queryBy : function(fn, scope){
971         var data = this.snapshot || this.data;
972         return data.filterBy(fn, scope||this);
973     },
974
975     /**
976      * Collects unique values for a particular dataIndex from this store.
977      * @param {String} dataIndex The property to collect
978      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
979      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
980      * @return {Array} An array of the unique values
981      **/
982     collect : function(dataIndex, allowNull, bypassFilter){
983         var d = (bypassFilter === true && this.snapshot) ?
984                 this.snapshot.items : this.data.items;
985         var v, sv, r = [], l = {};
986         for(var i = 0, len = d.length; i < len; i++){
987             v = d[i].data[dataIndex];
988             sv = String(v);
989             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
990                 l[sv] = true;
991                 r[r.length] = v;
992             }
993         }
994         return r;
995     },
996
997     /**
998      * Revert to a view of the Record cache with no filtering applied.
999      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1000      */
1001     clearFilter : function(suppressEvent){
1002         if(this.snapshot && this.snapshot != this.data){
1003             this.data = this.snapshot;
1004             delete this.snapshot;
1005             if(suppressEvent !== true){
1006                 this.fireEvent("datachanged", this);
1007             }
1008         }
1009     },
1010
1011     // private
1012     afterEdit : function(record){
1013         if(this.modified.indexOf(record) == -1){
1014             this.modified.push(record);
1015         }
1016         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1017     },
1018     
1019     // private
1020     afterReject : function(record){
1021         this.modified.remove(record);
1022         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1023     },
1024
1025     // private
1026     afterCommit : function(record){
1027         this.modified.remove(record);
1028         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1029     },
1030
1031     /**
1032      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1033      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1034      */
1035     commitChanges : function(){
1036         var m = this.modified.slice(0);
1037         this.modified = [];
1038         for(var i = 0, len = m.length; i < len; i++){
1039             m[i].commit();
1040         }
1041     },
1042
1043     /**
1044      * Cancel outstanding changes on all changed records.
1045      */
1046     rejectChanges : 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].reject();
1051         }
1052     },
1053
1054     onMetaChange : function(meta, rtype, o){
1055         this.recordType = rtype;
1056         this.fields = rtype.prototype.fields;
1057         delete this.snapshot;
1058         this.sortInfo = meta.sortInfo || this.sortInfo;
1059         this.modified = [];
1060         this.fireEvent('metachange', this, this.reader.meta);
1061     },
1062     
1063     moveIndex : function(data, type)
1064     {
1065         var index = this.indexOf(data);
1066         
1067         var newIndex = index + type;
1068         
1069         this.remove(data);
1070         
1071         this.insert(newIndex, data);
1072         
1073     }
1074 });/*
1075  * Based on:
1076  * Ext JS Library 1.1.1
1077  * Copyright(c) 2006-2007, Ext JS, LLC.
1078  *
1079  * Originally Released Under LGPL - original licence link has changed is not relivant.
1080  *
1081  * Fork - LGPL
1082  * <script type="text/javascript">
1083  */
1084
1085 /**
1086  * @class Roo.data.SimpleStore
1087  * @extends Roo.data.Store
1088  * Small helper class to make creating Stores from Array data easier.
1089  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1090  * @cfg {Array} fields An array of field definition objects, or field name strings.
1091  * @cfg {Array} data The multi-dimensional array of data
1092  * @constructor
1093  * @param {Object} config
1094  */
1095 Roo.data.SimpleStore = function(config){
1096     Roo.data.SimpleStore.superclass.constructor.call(this, {
1097         isLocal : true,
1098         reader: new Roo.data.ArrayReader({
1099                 id: config.id
1100             },
1101             Roo.data.Record.create(config.fields)
1102         ),
1103         proxy : new Roo.data.MemoryProxy(config.data)
1104     });
1105     this.load();
1106 };
1107 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1108  * Based on:
1109  * Ext JS Library 1.1.1
1110  * Copyright(c) 2006-2007, Ext JS, LLC.
1111  *
1112  * Originally Released Under LGPL - original licence link has changed is not relivant.
1113  *
1114  * Fork - LGPL
1115  * <script type="text/javascript">
1116  */
1117
1118 /**
1119 /**
1120  * @extends Roo.data.Store
1121  * @class Roo.data.JsonStore
1122  * Small helper class to make creating Stores for JSON data easier. <br/>
1123 <pre><code>
1124 var store = new Roo.data.JsonStore({
1125     url: 'get-images.php',
1126     root: 'images',
1127     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1128 });
1129 </code></pre>
1130  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1131  * JsonReader and HttpProxy (unless inline data is provided).</b>
1132  * @cfg {Array} fields An array of field definition objects, or field name strings.
1133  * @constructor
1134  * @param {Object} config
1135  */
1136 Roo.data.JsonStore = function(c){
1137     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1138         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1139         reader: new Roo.data.JsonReader(c, c.fields)
1140     }));
1141 };
1142 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1143  * Based on:
1144  * Ext JS Library 1.1.1
1145  * Copyright(c) 2006-2007, Ext JS, LLC.
1146  *
1147  * Originally Released Under LGPL - original licence link has changed is not relivant.
1148  *
1149  * Fork - LGPL
1150  * <script type="text/javascript">
1151  */
1152
1153  
1154 Roo.data.Field = function(config){
1155     if(typeof config == "string"){
1156         config = {name: config};
1157     }
1158     Roo.apply(this, config);
1159     
1160     if(!this.type){
1161         this.type = "auto";
1162     }
1163     
1164     var st = Roo.data.SortTypes;
1165     // named sortTypes are supported, here we look them up
1166     if(typeof this.sortType == "string"){
1167         this.sortType = st[this.sortType];
1168     }
1169     
1170     // set default sortType for strings and dates
1171     if(!this.sortType){
1172         switch(this.type){
1173             case "string":
1174                 this.sortType = st.asUCString;
1175                 break;
1176             case "date":
1177                 this.sortType = st.asDate;
1178                 break;
1179             default:
1180                 this.sortType = st.none;
1181         }
1182     }
1183
1184     // define once
1185     var stripRe = /[\$,%]/g;
1186
1187     // prebuilt conversion function for this field, instead of
1188     // switching every time we're reading a value
1189     if(!this.convert){
1190         var cv, dateFormat = this.dateFormat;
1191         switch(this.type){
1192             case "":
1193             case "auto":
1194             case undefined:
1195                 cv = function(v){ return v; };
1196                 break;
1197             case "string":
1198                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1199                 break;
1200             case "int":
1201                 cv = function(v){
1202                     return v !== undefined && v !== null && v !== '' ?
1203                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1204                     };
1205                 break;
1206             case "float":
1207                 cv = function(v){
1208                     return v !== undefined && v !== null && v !== '' ?
1209                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1210                     };
1211                 break;
1212             case "bool":
1213             case "boolean":
1214                 cv = function(v){ return v === true || v === "true" || v == 1; };
1215                 break;
1216             case "date":
1217                 cv = function(v){
1218                     if(!v){
1219                         return '';
1220                     }
1221                     if(v instanceof Date){
1222                         return v;
1223                     }
1224                     if(dateFormat){
1225                         if(dateFormat == "timestamp"){
1226                             return new Date(v*1000);
1227                         }
1228                         return Date.parseDate(v, dateFormat);
1229                     }
1230                     var parsed = Date.parse(v);
1231                     return parsed ? new Date(parsed) : null;
1232                 };
1233              break;
1234             
1235         }
1236         this.convert = cv;
1237     }
1238 };
1239
1240 Roo.data.Field.prototype = {
1241     dateFormat: null,
1242     defaultValue: "",
1243     mapping: null,
1244     sortType : null,
1245     sortDir : "ASC"
1246 };/*
1247  * Based on:
1248  * Ext JS Library 1.1.1
1249  * Copyright(c) 2006-2007, Ext JS, LLC.
1250  *
1251  * Originally Released Under LGPL - original licence link has changed is not relivant.
1252  *
1253  * Fork - LGPL
1254  * <script type="text/javascript">
1255  */
1256  
1257 // Base class for reading structured data from a data source.  This class is intended to be
1258 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1259
1260 /**
1261  * @class Roo.data.DataReader
1262  * Base class for reading structured data from a data source.  This class is intended to be
1263  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1264  */
1265
1266 Roo.data.DataReader = function(meta, recordType){
1267     
1268     this.meta = meta;
1269     
1270     this.recordType = recordType instanceof Array ? 
1271         Roo.data.Record.create(recordType) : recordType;
1272 };
1273
1274 Roo.data.DataReader.prototype = {
1275      /**
1276      * Create an empty record
1277      * @param {Object} data (optional) - overlay some values
1278      * @return {Roo.data.Record} record created.
1279      */
1280     newRow :  function(d) {
1281         var da =  {};
1282         this.recordType.prototype.fields.each(function(c) {
1283             switch( c.type) {
1284                 case 'int' : da[c.name] = 0; break;
1285                 case 'date' : da[c.name] = new Date(); break;
1286                 case 'float' : da[c.name] = 0.0; break;
1287                 case 'boolean' : da[c.name] = false; break;
1288                 default : da[c.name] = ""; break;
1289             }
1290             
1291         });
1292         return new this.recordType(Roo.apply(da, d));
1293     }
1294     
1295 };/*
1296  * Based on:
1297  * Ext JS Library 1.1.1
1298  * Copyright(c) 2006-2007, Ext JS, LLC.
1299  *
1300  * Originally Released Under LGPL - original licence link has changed is not relivant.
1301  *
1302  * Fork - LGPL
1303  * <script type="text/javascript">
1304  */
1305
1306 /**
1307  * @class Roo.data.DataProxy
1308  * @extends Roo.data.Observable
1309  * This class is an abstract base class for implementations which provide retrieval of
1310  * unformatted data objects.<br>
1311  * <p>
1312  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1313  * (of the appropriate type which knows how to parse the data object) to provide a block of
1314  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1315  * <p>
1316  * Custom implementations must implement the load method as described in
1317  * {@link Roo.data.HttpProxy#load}.
1318  */
1319 Roo.data.DataProxy = function(){
1320     this.addEvents({
1321         /**
1322          * @event beforeload
1323          * Fires before a network request is made to retrieve a data object.
1324          * @param {Object} This DataProxy object.
1325          * @param {Object} params The params parameter to the load function.
1326          */
1327         beforeload : true,
1328         /**
1329          * @event load
1330          * Fires before the load method's callback is called.
1331          * @param {Object} This DataProxy object.
1332          * @param {Object} o The data object.
1333          * @param {Object} arg The callback argument object passed to the load function.
1334          */
1335         load : true,
1336         /**
1337          * @event loadexception
1338          * Fires if an Exception occurs during data retrieval.
1339          * @param {Object} This DataProxy object.
1340          * @param {Object} o The data object.
1341          * @param {Object} arg The callback argument object passed to the load function.
1342          * @param {Object} e The Exception.
1343          */
1344         loadexception : true
1345     });
1346     Roo.data.DataProxy.superclass.constructor.call(this);
1347 };
1348
1349 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1350
1351     /**
1352      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1353      */
1354 /*
1355  * Based on:
1356  * Ext JS Library 1.1.1
1357  * Copyright(c) 2006-2007, Ext JS, LLC.
1358  *
1359  * Originally Released Under LGPL - original licence link has changed is not relivant.
1360  *
1361  * Fork - LGPL
1362  * <script type="text/javascript">
1363  */
1364 /**
1365  * @class Roo.data.MemoryProxy
1366  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1367  * to the Reader when its load method is called.
1368  * @constructor
1369  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1370  */
1371 Roo.data.MemoryProxy = function(data){
1372     if (data.data) {
1373         data = data.data;
1374     }
1375     Roo.data.MemoryProxy.superclass.constructor.call(this);
1376     this.data = data;
1377 };
1378
1379 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1380     
1381     /**
1382      * Load data from the requested source (in this case an in-memory
1383      * data object passed to the constructor), read the data object into
1384      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1385      * process that block using the passed callback.
1386      * @param {Object} params This parameter is not used by the MemoryProxy class.
1387      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1388      * object into a block of Roo.data.Records.
1389      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1390      * The function must be passed <ul>
1391      * <li>The Record block object</li>
1392      * <li>The "arg" argument from the load function</li>
1393      * <li>A boolean success indicator</li>
1394      * </ul>
1395      * @param {Object} scope The scope in which to call the callback
1396      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1397      */
1398     load : function(params, reader, callback, scope, arg){
1399         params = params || {};
1400         var result;
1401         try {
1402             result = reader.readRecords(params.data ? params.data :this.data);
1403         }catch(e){
1404             this.fireEvent("loadexception", this, arg, null, e);
1405             callback.call(scope, null, arg, false);
1406             return;
1407         }
1408         callback.call(scope, result, arg, true);
1409     },
1410     
1411     // private
1412     update : function(params, records){
1413         
1414     }
1415 });/*
1416  * Based on:
1417  * Ext JS Library 1.1.1
1418  * Copyright(c) 2006-2007, Ext JS, LLC.
1419  *
1420  * Originally Released Under LGPL - original licence link has changed is not relivant.
1421  *
1422  * Fork - LGPL
1423  * <script type="text/javascript">
1424  */
1425 /**
1426  * @class Roo.data.HttpProxy
1427  * @extends Roo.data.DataProxy
1428  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1429  * configured to reference a certain URL.<br><br>
1430  * <p>
1431  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1432  * from which the running page was served.<br><br>
1433  * <p>
1434  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1435  * <p>
1436  * Be aware that to enable the browser to parse an XML document, the server must set
1437  * the Content-Type header in the HTTP response to "text/xml".
1438  * @constructor
1439  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1440  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1441  * will be used to make the request.
1442  */
1443 Roo.data.HttpProxy = function(conn){
1444     Roo.data.HttpProxy.superclass.constructor.call(this);
1445     // is conn a conn config or a real conn?
1446     this.conn = conn;
1447     this.useAjax = !conn || !conn.events;
1448   
1449 };
1450
1451 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1452     // thse are take from connection...
1453     
1454     /**
1455      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1456      */
1457     /**
1458      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1459      * extra parameters to each request made by this object. (defaults to undefined)
1460      */
1461     /**
1462      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1463      *  to each request made by this object. (defaults to undefined)
1464      */
1465     /**
1466      * @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)
1467      */
1468     /**
1469      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1470      */
1471      /**
1472      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1473      * @type Boolean
1474      */
1475   
1476
1477     /**
1478      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1479      * @type Boolean
1480      */
1481     /**
1482      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1483      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1484      * a finer-grained basis than the DataProxy events.
1485      */
1486     getConnection : function(){
1487         return this.useAjax ? Roo.Ajax : this.conn;
1488     },
1489
1490     /**
1491      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1492      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1493      * process that block using the passed callback.
1494      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1495      * for the request to the remote server.
1496      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1497      * object into a block of Roo.data.Records.
1498      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1499      * The function must be passed <ul>
1500      * <li>The Record block object</li>
1501      * <li>The "arg" argument from the load function</li>
1502      * <li>A boolean success indicator</li>
1503      * </ul>
1504      * @param {Object} scope The scope in which to call the callback
1505      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1506      */
1507     load : function(params, reader, callback, scope, arg){
1508         if(this.fireEvent("beforeload", this, params) !== false){
1509             var  o = {
1510                 params : params || {},
1511                 request: {
1512                     callback : callback,
1513                     scope : scope,
1514                     arg : arg
1515                 },
1516                 reader: reader,
1517                 callback : this.loadResponse,
1518                 scope: this
1519             };
1520             if(this.useAjax){
1521                 Roo.applyIf(o, this.conn);
1522                 if(this.activeRequest){
1523                     Roo.Ajax.abort(this.activeRequest);
1524                 }
1525                 this.activeRequest = Roo.Ajax.request(o);
1526             }else{
1527                 this.conn.request(o);
1528             }
1529         }else{
1530             callback.call(scope||this, null, arg, false);
1531         }
1532     },
1533
1534     // private
1535     loadResponse : function(o, success, response){
1536         delete this.activeRequest;
1537         if(!success){
1538             this.fireEvent("loadexception", this, o, response);
1539             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1540             return;
1541         }
1542         var result;
1543         try {
1544             result = o.reader.read(response);
1545         }catch(e){
1546             this.fireEvent("loadexception", this, o, response, e);
1547             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1548             return;
1549         }
1550         
1551         this.fireEvent("load", this, o, o.request.arg);
1552         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1553     },
1554
1555     // private
1556     update : function(dataSet){
1557
1558     },
1559
1560     // private
1561     updateResponse : function(dataSet){
1562
1563     }
1564 });/*
1565  * Based on:
1566  * Ext JS Library 1.1.1
1567  * Copyright(c) 2006-2007, Ext JS, LLC.
1568  *
1569  * Originally Released Under LGPL - original licence link has changed is not relivant.
1570  *
1571  * Fork - LGPL
1572  * <script type="text/javascript">
1573  */
1574
1575 /**
1576  * @class Roo.data.ScriptTagProxy
1577  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1578  * other than the originating domain of the running page.<br><br>
1579  * <p>
1580  * <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
1581  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1582  * <p>
1583  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1584  * source code that is used as the source inside a &lt;script> tag.<br><br>
1585  * <p>
1586  * In order for the browser to process the returned data, the server must wrap the data object
1587  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1588  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1589  * depending on whether the callback name was passed:
1590  * <p>
1591  * <pre><code>
1592 boolean scriptTag = false;
1593 String cb = request.getParameter("callback");
1594 if (cb != null) {
1595     scriptTag = true;
1596     response.setContentType("text/javascript");
1597 } else {
1598     response.setContentType("application/x-json");
1599 }
1600 Writer out = response.getWriter();
1601 if (scriptTag) {
1602     out.write(cb + "(");
1603 }
1604 out.print(dataBlock.toJsonString());
1605 if (scriptTag) {
1606     out.write(");");
1607 }
1608 </pre></code>
1609  *
1610  * @constructor
1611  * @param {Object} config A configuration object.
1612  */
1613 Roo.data.ScriptTagProxy = function(config){
1614     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1615     Roo.apply(this, config);
1616     this.head = document.getElementsByTagName("head")[0];
1617 };
1618
1619 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1620
1621 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1622     /**
1623      * @cfg {String} url The URL from which to request the data object.
1624      */
1625     /**
1626      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1627      */
1628     timeout : 30000,
1629     /**
1630      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1631      * the server the name of the callback function set up by the load call to process the returned data object.
1632      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1633      * javascript output which calls this named function passing the data object as its only parameter.
1634      */
1635     callbackParam : "callback",
1636     /**
1637      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1638      * name to the request.
1639      */
1640     nocache : true,
1641
1642     /**
1643      * Load data from the configured URL, read the data object into
1644      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1645      * process that block using the passed callback.
1646      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1647      * for the request to the remote server.
1648      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1649      * object into a block of Roo.data.Records.
1650      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1651      * The function must be passed <ul>
1652      * <li>The Record block object</li>
1653      * <li>The "arg" argument from the load function</li>
1654      * <li>A boolean success indicator</li>
1655      * </ul>
1656      * @param {Object} scope The scope in which to call the callback
1657      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1658      */
1659     load : function(params, reader, callback, scope, arg){
1660         if(this.fireEvent("beforeload", this, params) !== false){
1661
1662             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1663
1664             var url = this.url;
1665             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1666             if(this.nocache){
1667                 url += "&_dc=" + (new Date().getTime());
1668             }
1669             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1670             var trans = {
1671                 id : transId,
1672                 cb : "stcCallback"+transId,
1673                 scriptId : "stcScript"+transId,
1674                 params : params,
1675                 arg : arg,
1676                 url : url,
1677                 callback : callback,
1678                 scope : scope,
1679                 reader : reader
1680             };
1681             var conn = this;
1682
1683             window[trans.cb] = function(o){
1684                 conn.handleResponse(o, trans);
1685             };
1686
1687             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1688
1689             if(this.autoAbort !== false){
1690                 this.abort();
1691             }
1692
1693             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1694
1695             var script = document.createElement("script");
1696             script.setAttribute("src", url);
1697             script.setAttribute("type", "text/javascript");
1698             script.setAttribute("id", trans.scriptId);
1699             this.head.appendChild(script);
1700
1701             this.trans = trans;
1702         }else{
1703             callback.call(scope||this, null, arg, false);
1704         }
1705     },
1706
1707     // private
1708     isLoading : function(){
1709         return this.trans ? true : false;
1710     },
1711
1712     /**
1713      * Abort the current server request.
1714      */
1715     abort : function(){
1716         if(this.isLoading()){
1717             this.destroyTrans(this.trans);
1718         }
1719     },
1720
1721     // private
1722     destroyTrans : function(trans, isLoaded){
1723         this.head.removeChild(document.getElementById(trans.scriptId));
1724         clearTimeout(trans.timeoutId);
1725         if(isLoaded){
1726             window[trans.cb] = undefined;
1727             try{
1728                 delete window[trans.cb];
1729             }catch(e){}
1730         }else{
1731             // if hasn't been loaded, wait for load to remove it to prevent script error
1732             window[trans.cb] = function(){
1733                 window[trans.cb] = undefined;
1734                 try{
1735                     delete window[trans.cb];
1736                 }catch(e){}
1737             };
1738         }
1739     },
1740
1741     // private
1742     handleResponse : function(o, trans){
1743         this.trans = false;
1744         this.destroyTrans(trans, true);
1745         var result;
1746         try {
1747             result = trans.reader.readRecords(o);
1748         }catch(e){
1749             this.fireEvent("loadexception", this, o, trans.arg, e);
1750             trans.callback.call(trans.scope||window, null, trans.arg, false);
1751             return;
1752         }
1753         this.fireEvent("load", this, o, trans.arg);
1754         trans.callback.call(trans.scope||window, result, trans.arg, true);
1755     },
1756
1757     // private
1758     handleFailure : function(trans){
1759         this.trans = false;
1760         this.destroyTrans(trans, false);
1761         this.fireEvent("loadexception", this, null, trans.arg);
1762         trans.callback.call(trans.scope||window, null, trans.arg, false);
1763     }
1764 });/*
1765  * Based on:
1766  * Ext JS Library 1.1.1
1767  * Copyright(c) 2006-2007, Ext JS, LLC.
1768  *
1769  * Originally Released Under LGPL - original licence link has changed is not relivant.
1770  *
1771  * Fork - LGPL
1772  * <script type="text/javascript">
1773  */
1774
1775 /**
1776  * @class Roo.data.JsonReader
1777  * @extends Roo.data.DataReader
1778  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1779  * based on mappings in a provided Roo.data.Record constructor.
1780  * 
1781  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1782  * in the reply previously. 
1783  * 
1784  * <p>
1785  * Example code:
1786  * <pre><code>
1787 var RecordDef = Roo.data.Record.create([
1788     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1789     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1790 ]);
1791 var myReader = new Roo.data.JsonReader({
1792     totalProperty: "results",    // The property which contains the total dataset size (optional)
1793     root: "rows",                // The property which contains an Array of row objects
1794     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1795 }, RecordDef);
1796 </code></pre>
1797  * <p>
1798  * This would consume a JSON file like this:
1799  * <pre><code>
1800 { 'results': 2, 'rows': [
1801     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1802     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1803 }
1804 </code></pre>
1805  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1806  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1807  * paged from the remote server.
1808  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1809  * @cfg {String} root name of the property which contains the Array of row objects.
1810  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1811  * @cfg {Array} fields Array of field definition objects
1812  * @constructor
1813  * Create a new JsonReader
1814  * @param {Object} meta Metadata configuration options
1815  * @param {Object} recordType Either an Array of field definition objects,
1816  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1817  */
1818 Roo.data.JsonReader = function(meta, recordType){
1819     
1820     meta = meta || {};
1821     // set some defaults:
1822     Roo.applyIf(meta, {
1823         totalProperty: 'total',
1824         successProperty : 'success',
1825         root : 'data',
1826         id : 'id'
1827     });
1828     
1829     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1830 };
1831 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1832     
1833     /**
1834      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1835      * Used by Store query builder to append _requestMeta to params.
1836      * 
1837      */
1838     metaFromRemote : false,
1839     /**
1840      * This method is only used by a DataProxy which has retrieved data from a remote server.
1841      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1842      * @return {Object} data A data block which is used by an Roo.data.Store object as
1843      * a cache of Roo.data.Records.
1844      */
1845     read : function(response){
1846         var json = response.responseText;
1847        
1848         var o = /* eval:var:o */ eval("("+json+")");
1849         if(!o) {
1850             throw {message: "JsonReader.read: Json object not found"};
1851         }
1852         
1853         if(o.metaData){
1854             
1855             delete this.ef;
1856             this.metaFromRemote = true;
1857             this.meta = o.metaData;
1858             this.recordType = Roo.data.Record.create(o.metaData.fields);
1859             this.onMetaChange(this.meta, this.recordType, o);
1860         }
1861         return this.readRecords(o);
1862     },
1863
1864     // private function a store will implement
1865     onMetaChange : function(meta, recordType, o){
1866
1867     },
1868
1869     /**
1870          * @ignore
1871          */
1872     simpleAccess: function(obj, subsc) {
1873         return obj[subsc];
1874     },
1875
1876         /**
1877          * @ignore
1878          */
1879     getJsonAccessor: function(){
1880         var re = /[\[\.]/;
1881         return function(expr) {
1882             try {
1883                 return(re.test(expr))
1884                     ? new Function("obj", "return obj." + expr)
1885                     : function(obj){
1886                         return obj[expr];
1887                     };
1888             } catch(e){}
1889             return Roo.emptyFn;
1890         };
1891     }(),
1892
1893     /**
1894      * Create a data block containing Roo.data.Records from an XML document.
1895      * @param {Object} o An object which contains an Array of row objects in the property specified
1896      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1897      * which contains the total size of the dataset.
1898      * @return {Object} data A data block which is used by an Roo.data.Store object as
1899      * a cache of Roo.data.Records.
1900      */
1901     readRecords : function(o){
1902         /**
1903          * After any data loads, the raw JSON data is available for further custom processing.
1904          * @type Object
1905          */
1906         this.o = o;
1907         var s = this.meta, Record = this.recordType,
1908             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1909
1910 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1911         if (!this.ef) {
1912             if(s.totalProperty) {
1913                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1914                 }
1915                 if(s.successProperty) {
1916                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1917                 }
1918                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1919                 if (s.id) {
1920                         var g = this.getJsonAccessor(s.id);
1921                         this.getId = function(rec) {
1922                                 var r = g(rec);  
1923                                 return (r === undefined || r === "") ? null : r;
1924                         };
1925                 } else {
1926                         this.getId = function(){return null;};
1927                 }
1928             this.ef = [];
1929             for(var jj = 0; jj < fl; jj++){
1930                 f = fi[jj];
1931                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1932                 this.ef[jj] = this.getJsonAccessor(map);
1933             }
1934         }
1935
1936         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1937         if(s.totalProperty){
1938             var vt = parseInt(this.getTotal(o), 10);
1939             if(!isNaN(vt)){
1940                 totalRecords = vt;
1941             }
1942         }
1943         if(s.successProperty){
1944             var vs = this.getSuccess(o);
1945             if(vs === false || vs === 'false'){
1946                 success = false;
1947             }
1948         }
1949         var records = [];
1950         for(var i = 0; i < c; i++){
1951                 var n = root[i];
1952             var values = {};
1953             var id = this.getId(n);
1954             for(var j = 0; j < fl; j++){
1955                 f = fi[j];
1956             var v = this.ef[j](n);
1957             if (!f.convert) {
1958                 Roo.log('missing convert for ' + f.name);
1959                 Roo.log(f);
1960                 continue;
1961             }
1962             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1963             }
1964             var record = new Record(values, id);
1965             record.json = n;
1966             records[i] = record;
1967         }
1968         return {
1969             raw : o,
1970             success : success,
1971             records : records,
1972             totalRecords : totalRecords
1973         };
1974     }
1975 });/*
1976  * Based on:
1977  * Ext JS Library 1.1.1
1978  * Copyright(c) 2006-2007, Ext JS, LLC.
1979  *
1980  * Originally Released Under LGPL - original licence link has changed is not relivant.
1981  *
1982  * Fork - LGPL
1983  * <script type="text/javascript">
1984  */
1985
1986 /**
1987  * @class Roo.data.XmlReader
1988  * @extends Roo.data.DataReader
1989  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
1990  * based on mappings in a provided Roo.data.Record constructor.<br><br>
1991  * <p>
1992  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
1993  * header in the HTTP response must be set to "text/xml".</em>
1994  * <p>
1995  * Example code:
1996  * <pre><code>
1997 var RecordDef = Roo.data.Record.create([
1998    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1999    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2000 ]);
2001 var myReader = new Roo.data.XmlReader({
2002    totalRecords: "results", // The element which contains the total dataset size (optional)
2003    record: "row",           // The repeated element which contains row information
2004    id: "id"                 // The element within the row that provides an ID for the record (optional)
2005 }, RecordDef);
2006 </code></pre>
2007  * <p>
2008  * This would consume an XML file like this:
2009  * <pre><code>
2010 &lt;?xml?>
2011 &lt;dataset>
2012  &lt;results>2&lt;/results>
2013  &lt;row>
2014    &lt;id>1&lt;/id>
2015    &lt;name>Bill&lt;/name>
2016    &lt;occupation>Gardener&lt;/occupation>
2017  &lt;/row>
2018  &lt;row>
2019    &lt;id>2&lt;/id>
2020    &lt;name>Ben&lt;/name>
2021    &lt;occupation>Horticulturalist&lt;/occupation>
2022  &lt;/row>
2023 &lt;/dataset>
2024 </code></pre>
2025  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2026  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2027  * paged from the remote server.
2028  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2029  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2030  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2031  * a record identifier value.
2032  * @constructor
2033  * Create a new XmlReader
2034  * @param {Object} meta Metadata configuration options
2035  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2036  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2037  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2038  */
2039 Roo.data.XmlReader = function(meta, recordType){
2040     meta = meta || {};
2041     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2042 };
2043 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2044     /**
2045      * This method is only used by a DataProxy which has retrieved data from a remote server.
2046          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2047          * to contain a method called 'responseXML' that returns an XML document object.
2048      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2049      * a cache of Roo.data.Records.
2050      */
2051     read : function(response){
2052         var doc = response.responseXML;
2053         if(!doc) {
2054             throw {message: "XmlReader.read: XML Document not available"};
2055         }
2056         return this.readRecords(doc);
2057     },
2058
2059     /**
2060      * Create a data block containing Roo.data.Records from an XML document.
2061          * @param {Object} doc A parsed XML document.
2062      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2063      * a cache of Roo.data.Records.
2064      */
2065     readRecords : function(doc){
2066         /**
2067          * After any data loads/reads, the raw XML Document is available for further custom processing.
2068          * @type XMLDocument
2069          */
2070         this.xmlData = doc;
2071         var root = doc.documentElement || doc;
2072         var q = Roo.DomQuery;
2073         var recordType = this.recordType, fields = recordType.prototype.fields;
2074         var sid = this.meta.id;
2075         var totalRecords = 0, success = true;
2076         if(this.meta.totalRecords){
2077             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2078         }
2079         
2080         if(this.meta.success){
2081             var sv = q.selectValue(this.meta.success, root, true);
2082             success = sv !== false && sv !== 'false';
2083         }
2084         var records = [];
2085         var ns = q.select(this.meta.record, root);
2086         for(var i = 0, len = ns.length; i < len; i++) {
2087                 var n = ns[i];
2088                 var values = {};
2089                 var id = sid ? q.selectValue(sid, n) : undefined;
2090                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2091                     var f = fields.items[j];
2092                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2093                     v = f.convert(v);
2094                     values[f.name] = v;
2095                 }
2096                 var record = new recordType(values, id);
2097                 record.node = n;
2098                 records[records.length] = record;
2099             }
2100
2101             return {
2102                 success : success,
2103                 records : records,
2104                 totalRecords : totalRecords || records.length
2105             };
2106     }
2107 });/*
2108  * Based on:
2109  * Ext JS Library 1.1.1
2110  * Copyright(c) 2006-2007, Ext JS, LLC.
2111  *
2112  * Originally Released Under LGPL - original licence link has changed is not relivant.
2113  *
2114  * Fork - LGPL
2115  * <script type="text/javascript">
2116  */
2117
2118 /**
2119  * @class Roo.data.ArrayReader
2120  * @extends Roo.data.DataReader
2121  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2122  * Each element of that Array represents a row of data fields. The
2123  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2124  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2125  * <p>
2126  * Example code:.
2127  * <pre><code>
2128 var RecordDef = Roo.data.Record.create([
2129     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2130     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2131 ]);
2132 var myReader = new Roo.data.ArrayReader({
2133     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2134 }, RecordDef);
2135 </code></pre>
2136  * <p>
2137  * This would consume an Array like this:
2138  * <pre><code>
2139 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2140   </code></pre>
2141  
2142  * @constructor
2143  * Create a new JsonReader
2144  * @param {Object} meta Metadata configuration options.
2145  * @param {Object|Array} recordType Either an Array of field definition objects
2146  * 
2147  * @cfg {Array} fields Array of field definition objects
2148  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2149  * as specified to {@link Roo.data.Record#create},
2150  * or an {@link Roo.data.Record} object
2151  *
2152  * 
2153  * created using {@link Roo.data.Record#create}.
2154  */
2155 Roo.data.ArrayReader = function(meta, recordType){
2156     
2157      
2158     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2159 };
2160
2161 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2162     /**
2163      * Create a data block containing Roo.data.Records from an XML document.
2164      * @param {Object} o An Array of row objects which represents the dataset.
2165      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2166      * a cache of Roo.data.Records.
2167      */
2168     readRecords : function(o){
2169         var sid = this.meta ? this.meta.id : null;
2170         var recordType = this.recordType, fields = recordType.prototype.fields;
2171         var records = [];
2172         var root = o;
2173             for(var i = 0; i < root.length; i++){
2174                     var n = root[i];
2175                 var values = {};
2176                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2177                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2178                 var f = fields.items[j];
2179                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2180                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2181                 v = f.convert(v);
2182                 values[f.name] = v;
2183             }
2184                 var record = new recordType(values, id);
2185                 record.json = n;
2186                 records[records.length] = record;
2187             }
2188             return {
2189                 records : records,
2190                 totalRecords : records.length
2191             };
2192     }
2193 });/*
2194  * Based on:
2195  * Ext JS Library 1.1.1
2196  * Copyright(c) 2006-2007, Ext JS, LLC.
2197  *
2198  * Originally Released Under LGPL - original licence link has changed is not relivant.
2199  *
2200  * Fork - LGPL
2201  * <script type="text/javascript">
2202  */
2203
2204
2205 /**
2206  * @class Roo.data.Tree
2207  * @extends Roo.util.Observable
2208  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2209  * in the tree have most standard DOM functionality.
2210  * @constructor
2211  * @param {Node} root (optional) The root node
2212  */
2213 Roo.data.Tree = function(root){
2214    this.nodeHash = {};
2215    /**
2216     * The root node for this tree
2217     * @type Node
2218     */
2219    this.root = null;
2220    if(root){
2221        this.setRootNode(root);
2222    }
2223    this.addEvents({
2224        /**
2225         * @event append
2226         * Fires when a new child node is appended to a node in this tree.
2227         * @param {Tree} tree The owner tree
2228         * @param {Node} parent The parent node
2229         * @param {Node} node The newly appended node
2230         * @param {Number} index The index of the newly appended node
2231         */
2232        "append" : true,
2233        /**
2234         * @event remove
2235         * Fires when a child node is removed from a node in this tree.
2236         * @param {Tree} tree The owner tree
2237         * @param {Node} parent The parent node
2238         * @param {Node} node The child node removed
2239         */
2240        "remove" : true,
2241        /**
2242         * @event move
2243         * Fires when a node is moved to a new location in the tree
2244         * @param {Tree} tree The owner tree
2245         * @param {Node} node The node moved
2246         * @param {Node} oldParent The old parent of this node
2247         * @param {Node} newParent The new parent of this node
2248         * @param {Number} index The index it was moved to
2249         */
2250        "move" : true,
2251        /**
2252         * @event insert
2253         * Fires when a new child node is inserted in a node in this tree.
2254         * @param {Tree} tree The owner tree
2255         * @param {Node} parent The parent node
2256         * @param {Node} node The child node inserted
2257         * @param {Node} refNode The child node the node was inserted before
2258         */
2259        "insert" : true,
2260        /**
2261         * @event beforeappend
2262         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2263         * @param {Tree} tree The owner tree
2264         * @param {Node} parent The parent node
2265         * @param {Node} node The child node to be appended
2266         */
2267        "beforeappend" : true,
2268        /**
2269         * @event beforeremove
2270         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2271         * @param {Tree} tree The owner tree
2272         * @param {Node} parent The parent node
2273         * @param {Node} node The child node to be removed
2274         */
2275        "beforeremove" : true,
2276        /**
2277         * @event beforemove
2278         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2279         * @param {Tree} tree The owner tree
2280         * @param {Node} node The node being moved
2281         * @param {Node} oldParent The parent of the node
2282         * @param {Node} newParent The new parent the node is moving to
2283         * @param {Number} index The index it is being moved to
2284         */
2285        "beforemove" : true,
2286        /**
2287         * @event beforeinsert
2288         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2289         * @param {Tree} tree The owner tree
2290         * @param {Node} parent The parent node
2291         * @param {Node} node The child node to be inserted
2292         * @param {Node} refNode The child node the node is being inserted before
2293         */
2294        "beforeinsert" : true
2295    });
2296
2297     Roo.data.Tree.superclass.constructor.call(this);
2298 };
2299
2300 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2301     pathSeparator: "/",
2302
2303     proxyNodeEvent : function(){
2304         return this.fireEvent.apply(this, arguments);
2305     },
2306
2307     /**
2308      * Returns the root node for this tree.
2309      * @return {Node}
2310      */
2311     getRootNode : function(){
2312         return this.root;
2313     },
2314
2315     /**
2316      * Sets the root node for this tree.
2317      * @param {Node} node
2318      * @return {Node}
2319      */
2320     setRootNode : function(node){
2321         this.root = node;
2322         node.ownerTree = this;
2323         node.isRoot = true;
2324         this.registerNode(node);
2325         return node;
2326     },
2327
2328     /**
2329      * Gets a node in this tree by its id.
2330      * @param {String} id
2331      * @return {Node}
2332      */
2333     getNodeById : function(id){
2334         return this.nodeHash[id];
2335     },
2336
2337     registerNode : function(node){
2338         this.nodeHash[node.id] = node;
2339     },
2340
2341     unregisterNode : function(node){
2342         delete this.nodeHash[node.id];
2343     },
2344
2345     toString : function(){
2346         return "[Tree"+(this.id?" "+this.id:"")+"]";
2347     }
2348 });
2349
2350 /**
2351  * @class Roo.data.Node
2352  * @extends Roo.util.Observable
2353  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2354  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2355  * @constructor
2356  * @param {Object} attributes The attributes/config for the node
2357  */
2358 Roo.data.Node = function(attributes){
2359     /**
2360      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2361      * @type {Object}
2362      */
2363     this.attributes = attributes || {};
2364     this.leaf = this.attributes.leaf;
2365     /**
2366      * The node id. @type String
2367      */
2368     this.id = this.attributes.id;
2369     if(!this.id){
2370         this.id = Roo.id(null, "ynode-");
2371         this.attributes.id = this.id;
2372     }
2373      
2374     
2375     /**
2376      * All child nodes of this node. @type Array
2377      */
2378     this.childNodes = [];
2379     if(!this.childNodes.indexOf){ // indexOf is a must
2380         this.childNodes.indexOf = function(o){
2381             for(var i = 0, len = this.length; i < len; i++){
2382                 if(this[i] == o) {
2383                     return i;
2384                 }
2385             }
2386             return -1;
2387         };
2388     }
2389     /**
2390      * The parent node for this node. @type Node
2391      */
2392     this.parentNode = null;
2393     /**
2394      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2395      */
2396     this.firstChild = null;
2397     /**
2398      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2399      */
2400     this.lastChild = null;
2401     /**
2402      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2403      */
2404     this.previousSibling = null;
2405     /**
2406      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2407      */
2408     this.nextSibling = null;
2409
2410     this.addEvents({
2411        /**
2412         * @event append
2413         * Fires when a new child node is appended
2414         * @param {Tree} tree The owner tree
2415         * @param {Node} this This node
2416         * @param {Node} node The newly appended node
2417         * @param {Number} index The index of the newly appended node
2418         */
2419        "append" : true,
2420        /**
2421         * @event remove
2422         * Fires when a child node is removed
2423         * @param {Tree} tree The owner tree
2424         * @param {Node} this This node
2425         * @param {Node} node The removed node
2426         */
2427        "remove" : true,
2428        /**
2429         * @event move
2430         * Fires when this node is moved to a new location in the tree
2431         * @param {Tree} tree The owner tree
2432         * @param {Node} this This node
2433         * @param {Node} oldParent The old parent of this node
2434         * @param {Node} newParent The new parent of this node
2435         * @param {Number} index The index it was moved to
2436         */
2437        "move" : true,
2438        /**
2439         * @event insert
2440         * Fires when a new child node is inserted.
2441         * @param {Tree} tree The owner tree
2442         * @param {Node} this This node
2443         * @param {Node} node The child node inserted
2444         * @param {Node} refNode The child node the node was inserted before
2445         */
2446        "insert" : true,
2447        /**
2448         * @event beforeappend
2449         * Fires before a new child is appended, return false to cancel the append.
2450         * @param {Tree} tree The owner tree
2451         * @param {Node} this This node
2452         * @param {Node} node The child node to be appended
2453         */
2454        "beforeappend" : true,
2455        /**
2456         * @event beforeremove
2457         * Fires before a child is removed, return false to cancel the remove.
2458         * @param {Tree} tree The owner tree
2459         * @param {Node} this This node
2460         * @param {Node} node The child node to be removed
2461         */
2462        "beforeremove" : true,
2463        /**
2464         * @event beforemove
2465         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2466         * @param {Tree} tree The owner tree
2467         * @param {Node} this This node
2468         * @param {Node} oldParent The parent of this node
2469         * @param {Node} newParent The new parent this node is moving to
2470         * @param {Number} index The index it is being moved to
2471         */
2472        "beforemove" : true,
2473        /**
2474         * @event beforeinsert
2475         * Fires before a new child is inserted, return false to cancel the insert.
2476         * @param {Tree} tree The owner tree
2477         * @param {Node} this This node
2478         * @param {Node} node The child node to be inserted
2479         * @param {Node} refNode The child node the node is being inserted before
2480         */
2481        "beforeinsert" : true
2482    });
2483     this.listeners = this.attributes.listeners;
2484     Roo.data.Node.superclass.constructor.call(this);
2485 };
2486
2487 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2488     fireEvent : function(evtName){
2489         // first do standard event for this node
2490         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2491             return false;
2492         }
2493         // then bubble it up to the tree if the event wasn't cancelled
2494         var ot = this.getOwnerTree();
2495         if(ot){
2496             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2497                 return false;
2498             }
2499         }
2500         return true;
2501     },
2502
2503     /**
2504      * Returns true if this node is a leaf
2505      * @return {Boolean}
2506      */
2507     isLeaf : function(){
2508         return this.leaf === true;
2509     },
2510
2511     // private
2512     setFirstChild : function(node){
2513         this.firstChild = node;
2514     },
2515
2516     //private
2517     setLastChild : function(node){
2518         this.lastChild = node;
2519     },
2520
2521
2522     /**
2523      * Returns true if this node is the last child of its parent
2524      * @return {Boolean}
2525      */
2526     isLast : function(){
2527        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2528     },
2529
2530     /**
2531      * Returns true if this node is the first child of its parent
2532      * @return {Boolean}
2533      */
2534     isFirst : function(){
2535        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2536     },
2537
2538     hasChildNodes : function(){
2539         return !this.isLeaf() && this.childNodes.length > 0;
2540     },
2541
2542     /**
2543      * Insert node(s) as the last child node of this node.
2544      * @param {Node/Array} node The node or Array of nodes to append
2545      * @return {Node} The appended node if single append, or null if an array was passed
2546      */
2547     appendChild : function(node){
2548         var multi = false;
2549         if(node instanceof Array){
2550             multi = node;
2551         }else if(arguments.length > 1){
2552             multi = arguments;
2553         }
2554         
2555         // if passed an array or multiple args do them one by one
2556         if(multi){
2557             for(var i = 0, len = multi.length; i < len; i++) {
2558                 this.appendChild(multi[i]);
2559             }
2560         }else{
2561             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2562                 return false;
2563             }
2564             var index = this.childNodes.length;
2565             var oldParent = node.parentNode;
2566             // it's a move, make sure we move it cleanly
2567             if(oldParent){
2568                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2569                     return false;
2570                 }
2571                 oldParent.removeChild(node);
2572             }
2573             
2574             index = this.childNodes.length;
2575             if(index == 0){
2576                 this.setFirstChild(node);
2577             }
2578             this.childNodes.push(node);
2579             node.parentNode = this;
2580             var ps = this.childNodes[index-1];
2581             if(ps){
2582                 node.previousSibling = ps;
2583                 ps.nextSibling = node;
2584             }else{
2585                 node.previousSibling = null;
2586             }
2587             node.nextSibling = null;
2588             this.setLastChild(node);
2589             node.setOwnerTree(this.getOwnerTree());
2590             this.fireEvent("append", this.ownerTree, this, node, index);
2591             if(this.ownerTree) {
2592                 this.ownerTree.fireEvent("appendnode", this, node, index);
2593             }
2594             if(oldParent){
2595                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2596             }
2597             return node;
2598         }
2599     },
2600
2601     /**
2602      * Removes a child node from this node.
2603      * @param {Node} node The node to remove
2604      * @return {Node} The removed node
2605      */
2606     removeChild : function(node){
2607         var index = this.childNodes.indexOf(node);
2608         if(index == -1){
2609             return false;
2610         }
2611         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2612             return false;
2613         }
2614
2615         // remove it from childNodes collection
2616         this.childNodes.splice(index, 1);
2617
2618         // update siblings
2619         if(node.previousSibling){
2620             node.previousSibling.nextSibling = node.nextSibling;
2621         }
2622         if(node.nextSibling){
2623             node.nextSibling.previousSibling = node.previousSibling;
2624         }
2625
2626         // update child refs
2627         if(this.firstChild == node){
2628             this.setFirstChild(node.nextSibling);
2629         }
2630         if(this.lastChild == node){
2631             this.setLastChild(node.previousSibling);
2632         }
2633
2634         node.setOwnerTree(null);
2635         // clear any references from the node
2636         node.parentNode = null;
2637         node.previousSibling = null;
2638         node.nextSibling = null;
2639         this.fireEvent("remove", this.ownerTree, this, node);
2640         return node;
2641     },
2642
2643     /**
2644      * Inserts the first node before the second node in this nodes childNodes collection.
2645      * @param {Node} node The node to insert
2646      * @param {Node} refNode The node to insert before (if null the node is appended)
2647      * @return {Node} The inserted node
2648      */
2649     insertBefore : function(node, refNode){
2650         if(!refNode){ // like standard Dom, refNode can be null for append
2651             return this.appendChild(node);
2652         }
2653         // nothing to do
2654         if(node == refNode){
2655             return false;
2656         }
2657
2658         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2659             return false;
2660         }
2661         var index = this.childNodes.indexOf(refNode);
2662         var oldParent = node.parentNode;
2663         var refIndex = index;
2664
2665         // when moving internally, indexes will change after remove
2666         if(oldParent == this && this.childNodes.indexOf(node) < index){
2667             refIndex--;
2668         }
2669
2670         // it's a move, make sure we move it cleanly
2671         if(oldParent){
2672             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2673                 return false;
2674             }
2675             oldParent.removeChild(node);
2676         }
2677         if(refIndex == 0){
2678             this.setFirstChild(node);
2679         }
2680         this.childNodes.splice(refIndex, 0, node);
2681         node.parentNode = this;
2682         var ps = this.childNodes[refIndex-1];
2683         if(ps){
2684             node.previousSibling = ps;
2685             ps.nextSibling = node;
2686         }else{
2687             node.previousSibling = null;
2688         }
2689         node.nextSibling = refNode;
2690         refNode.previousSibling = node;
2691         node.setOwnerTree(this.getOwnerTree());
2692         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2693         if(oldParent){
2694             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2695         }
2696         return node;
2697     },
2698
2699     /**
2700      * Returns the child node at the specified index.
2701      * @param {Number} index
2702      * @return {Node}
2703      */
2704     item : function(index){
2705         return this.childNodes[index];
2706     },
2707
2708     /**
2709      * Replaces one child node in this node with another.
2710      * @param {Node} newChild The replacement node
2711      * @param {Node} oldChild The node to replace
2712      * @return {Node} The replaced node
2713      */
2714     replaceChild : function(newChild, oldChild){
2715         this.insertBefore(newChild, oldChild);
2716         this.removeChild(oldChild);
2717         return oldChild;
2718     },
2719
2720     /**
2721      * Returns the index of a child node
2722      * @param {Node} node
2723      * @return {Number} The index of the node or -1 if it was not found
2724      */
2725     indexOf : function(child){
2726         return this.childNodes.indexOf(child);
2727     },
2728
2729     /**
2730      * Returns the tree this node is in.
2731      * @return {Tree}
2732      */
2733     getOwnerTree : function(){
2734         // if it doesn't have one, look for one
2735         if(!this.ownerTree){
2736             var p = this;
2737             while(p){
2738                 if(p.ownerTree){
2739                     this.ownerTree = p.ownerTree;
2740                     break;
2741                 }
2742                 p = p.parentNode;
2743             }
2744         }
2745         return this.ownerTree;
2746     },
2747
2748     /**
2749      * Returns depth of this node (the root node has a depth of 0)
2750      * @return {Number}
2751      */
2752     getDepth : function(){
2753         var depth = 0;
2754         var p = this;
2755         while(p.parentNode){
2756             ++depth;
2757             p = p.parentNode;
2758         }
2759         return depth;
2760     },
2761
2762     // private
2763     setOwnerTree : function(tree){
2764         // if it's move, we need to update everyone
2765         if(tree != this.ownerTree){
2766             if(this.ownerTree){
2767                 this.ownerTree.unregisterNode(this);
2768             }
2769             this.ownerTree = tree;
2770             var cs = this.childNodes;
2771             for(var i = 0, len = cs.length; i < len; i++) {
2772                 cs[i].setOwnerTree(tree);
2773             }
2774             if(tree){
2775                 tree.registerNode(this);
2776             }
2777         }
2778     },
2779
2780     /**
2781      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2782      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2783      * @return {String} The path
2784      */
2785     getPath : function(attr){
2786         attr = attr || "id";
2787         var p = this.parentNode;
2788         var b = [this.attributes[attr]];
2789         while(p){
2790             b.unshift(p.attributes[attr]);
2791             p = p.parentNode;
2792         }
2793         var sep = this.getOwnerTree().pathSeparator;
2794         return sep + b.join(sep);
2795     },
2796
2797     /**
2798      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2799      * function call will be the scope provided or the current node. The arguments to the function
2800      * will be the args provided or the current node. If the function returns false at any point,
2801      * the bubble is stopped.
2802      * @param {Function} fn The function to call
2803      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2804      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2805      */
2806     bubble : function(fn, scope, args){
2807         var p = this;
2808         while(p){
2809             if(fn.call(scope || p, args || p) === false){
2810                 break;
2811             }
2812             p = p.parentNode;
2813         }
2814     },
2815
2816     /**
2817      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2818      * function call will be the scope provided or the current node. The arguments to the function
2819      * will be the args provided or the current node. If the function returns false at any point,
2820      * the cascade is stopped on that branch.
2821      * @param {Function} fn The function to call
2822      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2823      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2824      */
2825     cascade : function(fn, scope, args){
2826         if(fn.call(scope || this, args || this) !== false){
2827             var cs = this.childNodes;
2828             for(var i = 0, len = cs.length; i < len; i++) {
2829                 cs[i].cascade(fn, scope, args);
2830             }
2831         }
2832     },
2833
2834     /**
2835      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2836      * function call will be the scope provided or the current node. The arguments to the function
2837      * will be the args provided or the current node. If the function returns false at any point,
2838      * the iteration stops.
2839      * @param {Function} fn The function to call
2840      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2841      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2842      */
2843     eachChild : function(fn, scope, args){
2844         var cs = this.childNodes;
2845         for(var i = 0, len = cs.length; i < len; i++) {
2846                 if(fn.call(scope || this, args || cs[i]) === false){
2847                     break;
2848                 }
2849         }
2850     },
2851
2852     /**
2853      * Finds the first child that has the attribute with the specified value.
2854      * @param {String} attribute The attribute name
2855      * @param {Mixed} value The value to search for
2856      * @return {Node} The found child or null if none was found
2857      */
2858     findChild : function(attribute, value){
2859         var cs = this.childNodes;
2860         for(var i = 0, len = cs.length; i < len; i++) {
2861                 if(cs[i].attributes[attribute] == value){
2862                     return cs[i];
2863                 }
2864         }
2865         return null;
2866     },
2867
2868     /**
2869      * Finds the first child by a custom function. The child matches if the function passed
2870      * returns true.
2871      * @param {Function} fn
2872      * @param {Object} scope (optional)
2873      * @return {Node} The found child or null if none was found
2874      */
2875     findChildBy : function(fn, scope){
2876         var cs = this.childNodes;
2877         for(var i = 0, len = cs.length; i < len; i++) {
2878                 if(fn.call(scope||cs[i], cs[i]) === true){
2879                     return cs[i];
2880                 }
2881         }
2882         return null;
2883     },
2884
2885     /**
2886      * Sorts this nodes children using the supplied sort function
2887      * @param {Function} fn
2888      * @param {Object} scope (optional)
2889      */
2890     sort : function(fn, scope){
2891         var cs = this.childNodes;
2892         var len = cs.length;
2893         if(len > 0){
2894             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2895             cs.sort(sortFn);
2896             for(var i = 0; i < len; i++){
2897                 var n = cs[i];
2898                 n.previousSibling = cs[i-1];
2899                 n.nextSibling = cs[i+1];
2900                 if(i == 0){
2901                     this.setFirstChild(n);
2902                 }
2903                 if(i == len-1){
2904                     this.setLastChild(n);
2905                 }
2906             }
2907         }
2908     },
2909
2910     /**
2911      * Returns true if this node is an ancestor (at any point) of the passed node.
2912      * @param {Node} node
2913      * @return {Boolean}
2914      */
2915     contains : function(node){
2916         return node.isAncestor(this);
2917     },
2918
2919     /**
2920      * Returns true if the passed node is an ancestor (at any point) of this node.
2921      * @param {Node} node
2922      * @return {Boolean}
2923      */
2924     isAncestor : function(node){
2925         var p = this.parentNode;
2926         while(p){
2927             if(p == node){
2928                 return true;
2929             }
2930             p = p.parentNode;
2931         }
2932         return false;
2933     },
2934
2935     toString : function(){
2936         return "[Node"+(this.id?" "+this.id:"")+"]";
2937     }
2938 });/*
2939  * Based on:
2940  * Ext JS Library 1.1.1
2941  * Copyright(c) 2006-2007, Ext JS, LLC.
2942  *
2943  * Originally Released Under LGPL - original licence link has changed is not relivant.
2944  *
2945  * Fork - LGPL
2946  * <script type="text/javascript">
2947  */
2948  (function(){ 
2949 /**
2950  * @class Roo.Layer
2951  * @extends Roo.Element
2952  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2953  * automatic maintaining of shadow/shim positions.
2954  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
2955  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
2956  * you can pass a string with a CSS class name. False turns off the shadow.
2957  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
2958  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
2959  * @cfg {String} cls CSS class to add to the element
2960  * @cfg {Number} zindex Starting z-index (defaults to 11000)
2961  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
2962  * @constructor
2963  * @param {Object} config An object with config options.
2964  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
2965  */
2966
2967 Roo.Layer = function(config, existingEl){
2968     config = config || {};
2969     var dh = Roo.DomHelper;
2970     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
2971     if(existingEl){
2972         this.dom = Roo.getDom(existingEl);
2973     }
2974     if(!this.dom){
2975         var o = config.dh || {tag: "div", cls: "x-layer"};
2976         this.dom = dh.append(pel, o);
2977     }
2978     if(config.cls){
2979         this.addClass(config.cls);
2980     }
2981     this.constrain = config.constrain !== false;
2982     this.visibilityMode = Roo.Element.VISIBILITY;
2983     if(config.id){
2984         this.id = this.dom.id = config.id;
2985     }else{
2986         this.id = Roo.id(this.dom);
2987     }
2988     this.zindex = config.zindex || this.getZIndex();
2989     this.position("absolute", this.zindex);
2990     if(config.shadow){
2991         this.shadowOffset = config.shadowOffset || 4;
2992         this.shadow = new Roo.Shadow({
2993             offset : this.shadowOffset,
2994             mode : config.shadow
2995         });
2996     }else{
2997         this.shadowOffset = 0;
2998     }
2999     this.useShim = config.shim !== false && Roo.useShims;
3000     this.useDisplay = config.useDisplay;
3001     this.hide();
3002 };
3003
3004 var supr = Roo.Element.prototype;
3005
3006 // shims are shared among layer to keep from having 100 iframes
3007 var shims = [];
3008
3009 Roo.extend(Roo.Layer, Roo.Element, {
3010
3011     getZIndex : function(){
3012         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
3013     },
3014
3015     getShim : function(){
3016         if(!this.useShim){
3017             return null;
3018         }
3019         if(this.shim){
3020             return this.shim;
3021         }
3022         var shim = shims.shift();
3023         if(!shim){
3024             shim = this.createShim();
3025             shim.enableDisplayMode('block');
3026             shim.dom.style.display = 'none';
3027             shim.dom.style.visibility = 'visible';
3028         }
3029         var pn = this.dom.parentNode;
3030         if(shim.dom.parentNode != pn){
3031             pn.insertBefore(shim.dom, this.dom);
3032         }
3033         shim.setStyle('z-index', this.getZIndex()-2);
3034         this.shim = shim;
3035         return shim;
3036     },
3037
3038     hideShim : function(){
3039         if(this.shim){
3040             this.shim.setDisplayed(false);
3041             shims.push(this.shim);
3042             delete this.shim;
3043         }
3044     },
3045
3046     disableShadow : function(){
3047         if(this.shadow){
3048             this.shadowDisabled = true;
3049             this.shadow.hide();
3050             this.lastShadowOffset = this.shadowOffset;
3051             this.shadowOffset = 0;
3052         }
3053     },
3054
3055     enableShadow : function(show){
3056         if(this.shadow){
3057             this.shadowDisabled = false;
3058             this.shadowOffset = this.lastShadowOffset;
3059             delete this.lastShadowOffset;
3060             if(show){
3061                 this.sync(true);
3062             }
3063         }
3064     },
3065
3066     // private
3067     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3068     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3069     sync : function(doShow){
3070         var sw = this.shadow;
3071         if(!this.updating && this.isVisible() && (sw || this.useShim)){
3072             var sh = this.getShim();
3073
3074             var w = this.getWidth(),
3075                 h = this.getHeight();
3076
3077             var l = this.getLeft(true),
3078                 t = this.getTop(true);
3079
3080             if(sw && !this.shadowDisabled){
3081                 if(doShow && !sw.isVisible()){
3082                     sw.show(this);
3083                 }else{
3084                     sw.realign(l, t, w, h);
3085                 }
3086                 if(sh){
3087                     if(doShow){
3088                        sh.show();
3089                     }
3090                     // fit the shim behind the shadow, so it is shimmed too
3091                     var a = sw.adjusts, s = sh.dom.style;
3092                     s.left = (Math.min(l, l+a.l))+"px";
3093                     s.top = (Math.min(t, t+a.t))+"px";
3094                     s.width = (w+a.w)+"px";
3095                     s.height = (h+a.h)+"px";
3096                 }
3097             }else if(sh){
3098                 if(doShow){
3099                    sh.show();
3100                 }
3101                 sh.setSize(w, h);
3102                 sh.setLeftTop(l, t);
3103             }
3104             
3105         }
3106     },
3107
3108     // private
3109     destroy : function(){
3110         this.hideShim();
3111         if(this.shadow){
3112             this.shadow.hide();
3113         }
3114         this.removeAllListeners();
3115         var pn = this.dom.parentNode;
3116         if(pn){
3117             pn.removeChild(this.dom);
3118         }
3119         Roo.Element.uncache(this.id);
3120     },
3121
3122     remove : function(){
3123         this.destroy();
3124     },
3125
3126     // private
3127     beginUpdate : function(){
3128         this.updating = true;
3129     },
3130
3131     // private
3132     endUpdate : function(){
3133         this.updating = false;
3134         this.sync(true);
3135     },
3136
3137     // private
3138     hideUnders : function(negOffset){
3139         if(this.shadow){
3140             this.shadow.hide();
3141         }
3142         this.hideShim();
3143     },
3144
3145     // private
3146     constrainXY : function(){
3147         if(this.constrain){
3148             var vw = Roo.lib.Dom.getViewWidth(),
3149                 vh = Roo.lib.Dom.getViewHeight();
3150             var s = Roo.get(document).getScroll();
3151
3152             var xy = this.getXY();
3153             var x = xy[0], y = xy[1];   
3154             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3155             // only move it if it needs it
3156             var moved = false;
3157             // first validate right/bottom
3158             if((x + w) > vw+s.left){
3159                 x = vw - w - this.shadowOffset;
3160                 moved = true;
3161             }
3162             if((y + h) > vh+s.top){
3163                 y = vh - h - this.shadowOffset;
3164                 moved = true;
3165             }
3166             // then make sure top/left isn't negative
3167             if(x < s.left){
3168                 x = s.left;
3169                 moved = true;
3170             }
3171             if(y < s.top){
3172                 y = s.top;
3173                 moved = true;
3174             }
3175             if(moved){
3176                 if(this.avoidY){
3177                     var ay = this.avoidY;
3178                     if(y <= ay && (y+h) >= ay){
3179                         y = ay-h-5;   
3180                     }
3181                 }
3182                 xy = [x, y];
3183                 this.storeXY(xy);
3184                 supr.setXY.call(this, xy);
3185                 this.sync();
3186             }
3187         }
3188     },
3189
3190     isVisible : function(){
3191         return this.visible;    
3192     },
3193
3194     // private
3195     showAction : function(){
3196         this.visible = true; // track visibility to prevent getStyle calls
3197         if(this.useDisplay === true){
3198             this.setDisplayed("");
3199         }else if(this.lastXY){
3200             supr.setXY.call(this, this.lastXY);
3201         }else if(this.lastLT){
3202             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3203         }
3204     },
3205
3206     // private
3207     hideAction : function(){
3208         this.visible = false;
3209         if(this.useDisplay === true){
3210             this.setDisplayed(false);
3211         }else{
3212             this.setLeftTop(-10000,-10000);
3213         }
3214     },
3215
3216     // overridden Element method
3217     setVisible : function(v, a, d, c, e){
3218         if(v){
3219             this.showAction();
3220         }
3221         if(a && v){
3222             var cb = function(){
3223                 this.sync(true);
3224                 if(c){
3225                     c();
3226                 }
3227             }.createDelegate(this);
3228             supr.setVisible.call(this, true, true, d, cb, e);
3229         }else{
3230             if(!v){
3231                 this.hideUnders(true);
3232             }
3233             var cb = c;
3234             if(a){
3235                 cb = function(){
3236                     this.hideAction();
3237                     if(c){
3238                         c();
3239                     }
3240                 }.createDelegate(this);
3241             }
3242             supr.setVisible.call(this, v, a, d, cb, e);
3243             if(v){
3244                 this.sync(true);
3245             }else if(!a){
3246                 this.hideAction();
3247             }
3248         }
3249     },
3250
3251     storeXY : function(xy){
3252         delete this.lastLT;
3253         this.lastXY = xy;
3254     },
3255
3256     storeLeftTop : function(left, top){
3257         delete this.lastXY;
3258         this.lastLT = [left, top];
3259     },
3260
3261     // private
3262     beforeFx : function(){
3263         this.beforeAction();
3264         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3265     },
3266
3267     // private
3268     afterFx : function(){
3269         Roo.Layer.superclass.afterFx.apply(this, arguments);
3270         this.sync(this.isVisible());
3271     },
3272
3273     // private
3274     beforeAction : function(){
3275         if(!this.updating && this.shadow){
3276             this.shadow.hide();
3277         }
3278     },
3279
3280     // overridden Element method
3281     setLeft : function(left){
3282         this.storeLeftTop(left, this.getTop(true));
3283         supr.setLeft.apply(this, arguments);
3284         this.sync();
3285     },
3286
3287     setTop : function(top){
3288         this.storeLeftTop(this.getLeft(true), top);
3289         supr.setTop.apply(this, arguments);
3290         this.sync();
3291     },
3292
3293     setLeftTop : function(left, top){
3294         this.storeLeftTop(left, top);
3295         supr.setLeftTop.apply(this, arguments);
3296         this.sync();
3297     },
3298
3299     setXY : function(xy, a, d, c, e){
3300         this.fixDisplay();
3301         this.beforeAction();
3302         this.storeXY(xy);
3303         var cb = this.createCB(c);
3304         supr.setXY.call(this, xy, a, d, cb, e);
3305         if(!a){
3306             cb();
3307         }
3308     },
3309
3310     // private
3311     createCB : function(c){
3312         var el = this;
3313         return function(){
3314             el.constrainXY();
3315             el.sync(true);
3316             if(c){
3317                 c();
3318             }
3319         };
3320     },
3321
3322     // overridden Element method
3323     setX : function(x, a, d, c, e){
3324         this.setXY([x, this.getY()], a, d, c, e);
3325     },
3326
3327     // overridden Element method
3328     setY : function(y, a, d, c, e){
3329         this.setXY([this.getX(), y], a, d, c, e);
3330     },
3331
3332     // overridden Element method
3333     setSize : function(w, h, a, d, c, e){
3334         this.beforeAction();
3335         var cb = this.createCB(c);
3336         supr.setSize.call(this, w, h, a, d, cb, e);
3337         if(!a){
3338             cb();
3339         }
3340     },
3341
3342     // overridden Element method
3343     setWidth : function(w, a, d, c, e){
3344         this.beforeAction();
3345         var cb = this.createCB(c);
3346         supr.setWidth.call(this, w, a, d, cb, e);
3347         if(!a){
3348             cb();
3349         }
3350     },
3351
3352     // overridden Element method
3353     setHeight : function(h, a, d, c, e){
3354         this.beforeAction();
3355         var cb = this.createCB(c);
3356         supr.setHeight.call(this, h, a, d, cb, e);
3357         if(!a){
3358             cb();
3359         }
3360     },
3361
3362     // overridden Element method
3363     setBounds : function(x, y, w, h, a, d, c, e){
3364         this.beforeAction();
3365         var cb = this.createCB(c);
3366         if(!a){
3367             this.storeXY([x, y]);
3368             supr.setXY.call(this, [x, y]);
3369             supr.setSize.call(this, w, h, a, d, cb, e);
3370             cb();
3371         }else{
3372             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3373         }
3374         return this;
3375     },
3376     
3377     /**
3378      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3379      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3380      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3381      * @param {Number} zindex The new z-index to set
3382      * @return {this} The Layer
3383      */
3384     setZIndex : function(zindex){
3385         this.zindex = zindex;
3386         this.setStyle("z-index", zindex + 2);
3387         if(this.shadow){
3388             this.shadow.setZIndex(zindex + 1);
3389         }
3390         if(this.shim){
3391             this.shim.setStyle("z-index", zindex);
3392         }
3393     }
3394 });
3395 })();/*
3396  * Based on:
3397  * Ext JS Library 1.1.1
3398  * Copyright(c) 2006-2007, Ext JS, LLC.
3399  *
3400  * Originally Released Under LGPL - original licence link has changed is not relivant.
3401  *
3402  * Fork - LGPL
3403  * <script type="text/javascript">
3404  */
3405
3406
3407 /**
3408  * @class Roo.Shadow
3409  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3410  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3411  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3412  * @constructor
3413  * Create a new Shadow
3414  * @param {Object} config The config object
3415  */
3416 Roo.Shadow = function(config){
3417     Roo.apply(this, config);
3418     if(typeof this.mode != "string"){
3419         this.mode = this.defaultMode;
3420     }
3421     var o = this.offset, a = {h: 0};
3422     var rad = Math.floor(this.offset/2);
3423     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3424         case "drop":
3425             a.w = 0;
3426             a.l = a.t = o;
3427             a.t -= 1;
3428             if(Roo.isIE){
3429                 a.l -= this.offset + rad;
3430                 a.t -= this.offset + rad;
3431                 a.w -= rad;
3432                 a.h -= rad;
3433                 a.t += 1;
3434             }
3435         break;
3436         case "sides":
3437             a.w = (o*2);
3438             a.l = -o;
3439             a.t = o-1;
3440             if(Roo.isIE){
3441                 a.l -= (this.offset - rad);
3442                 a.t -= this.offset + rad;
3443                 a.l += 1;
3444                 a.w -= (this.offset - rad)*2;
3445                 a.w -= rad + 1;
3446                 a.h -= 1;
3447             }
3448         break;
3449         case "frame":
3450             a.w = a.h = (o*2);
3451             a.l = a.t = -o;
3452             a.t += 1;
3453             a.h -= 2;
3454             if(Roo.isIE){
3455                 a.l -= (this.offset - rad);
3456                 a.t -= (this.offset - rad);
3457                 a.l += 1;
3458                 a.w -= (this.offset + rad + 1);
3459                 a.h -= (this.offset + rad);
3460                 a.h += 1;
3461             }
3462         break;
3463     };
3464
3465     this.adjusts = a;
3466 };
3467
3468 Roo.Shadow.prototype = {
3469     /**
3470      * @cfg {String} mode
3471      * The shadow display mode.  Supports the following options:<br />
3472      * sides: Shadow displays on both sides and bottom only<br />
3473      * frame: Shadow displays equally on all four sides<br />
3474      * drop: Traditional bottom-right drop shadow (default)
3475      */
3476     /**
3477      * @cfg {String} offset
3478      * The number of pixels to offset the shadow from the element (defaults to 4)
3479      */
3480     offset: 4,
3481
3482     // private
3483     defaultMode: "drop",
3484
3485     /**
3486      * Displays the shadow under the target element
3487      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3488      */
3489     show : function(target){
3490         target = Roo.get(target);
3491         if(!this.el){
3492             this.el = Roo.Shadow.Pool.pull();
3493             if(this.el.dom.nextSibling != target.dom){
3494                 this.el.insertBefore(target);
3495             }
3496         }
3497         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3498         if(Roo.isIE){
3499             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3500         }
3501         this.realign(
3502             target.getLeft(true),
3503             target.getTop(true),
3504             target.getWidth(),
3505             target.getHeight()
3506         );
3507         this.el.dom.style.display = "block";
3508     },
3509
3510     /**
3511      * Returns true if the shadow is visible, else false
3512      */
3513     isVisible : function(){
3514         return this.el ? true : false;  
3515     },
3516
3517     /**
3518      * Direct alignment when values are already available. Show must be called at least once before
3519      * calling this method to ensure it is initialized.
3520      * @param {Number} left The target element left position
3521      * @param {Number} top The target element top position
3522      * @param {Number} width The target element width
3523      * @param {Number} height The target element height
3524      */
3525     realign : function(l, t, w, h){
3526         if(!this.el){
3527             return;
3528         }
3529         var a = this.adjusts, d = this.el.dom, s = d.style;
3530         var iea = 0;
3531         s.left = (l+a.l)+"px";
3532         s.top = (t+a.t)+"px";
3533         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3534  
3535         if(s.width != sws || s.height != shs){
3536             s.width = sws;
3537             s.height = shs;
3538             if(!Roo.isIE){
3539                 var cn = d.childNodes;
3540                 var sww = Math.max(0, (sw-12))+"px";
3541                 cn[0].childNodes[1].style.width = sww;
3542                 cn[1].childNodes[1].style.width = sww;
3543                 cn[2].childNodes[1].style.width = sww;
3544                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3545             }
3546         }
3547     },
3548
3549     /**
3550      * Hides this shadow
3551      */
3552     hide : function(){
3553         if(this.el){
3554             this.el.dom.style.display = "none";
3555             Roo.Shadow.Pool.push(this.el);
3556             delete this.el;
3557         }
3558     },
3559
3560     /**
3561      * Adjust the z-index of this shadow
3562      * @param {Number} zindex The new z-index
3563      */
3564     setZIndex : function(z){
3565         this.zIndex = z;
3566         if(this.el){
3567             this.el.setStyle("z-index", z);
3568         }
3569     }
3570 };
3571
3572 // Private utility class that manages the internal Shadow cache
3573 Roo.Shadow.Pool = function(){
3574     var p = [];
3575     var markup = Roo.isIE ?
3576                  '<div class="x-ie-shadow"></div>' :
3577                  '<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>';
3578     return {
3579         pull : function(){
3580             var sh = p.shift();
3581             if(!sh){
3582                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3583                 sh.autoBoxAdjust = false;
3584             }
3585             return sh;
3586         },
3587
3588         push : function(sh){
3589             p.push(sh);
3590         }
3591     };
3592 }();/*
3593  * Based on:
3594  * Ext JS Library 1.1.1
3595  * Copyright(c) 2006-2007, Ext JS, LLC.
3596  *
3597  * Originally Released Under LGPL - original licence link has changed is not relivant.
3598  *
3599  * Fork - LGPL
3600  * <script type="text/javascript">
3601  */
3602
3603
3604 /**
3605  * @class Roo.SplitBar
3606  * @extends Roo.util.Observable
3607  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3608  * <br><br>
3609  * Usage:
3610  * <pre><code>
3611 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3612                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3613 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3614 split.minSize = 100;
3615 split.maxSize = 600;
3616 split.animate = true;
3617 split.on('moved', splitterMoved);
3618 </code></pre>
3619  * @constructor
3620  * Create a new SplitBar
3621  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3622  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3623  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3624  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3625                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3626                         position of the SplitBar).
3627  */
3628 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3629     
3630     /** @private */
3631     this.el = Roo.get(dragElement, true);
3632     this.el.dom.unselectable = "on";
3633     /** @private */
3634     this.resizingEl = Roo.get(resizingElement, true);
3635
3636     /**
3637      * @private
3638      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3639      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3640      * @type Number
3641      */
3642     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3643     
3644     /**
3645      * The minimum size of the resizing element. (Defaults to 0)
3646      * @type Number
3647      */
3648     this.minSize = 0;
3649     
3650     /**
3651      * The maximum size of the resizing element. (Defaults to 2000)
3652      * @type Number
3653      */
3654     this.maxSize = 2000;
3655     
3656     /**
3657      * Whether to animate the transition to the new size
3658      * @type Boolean
3659      */
3660     this.animate = false;
3661     
3662     /**
3663      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3664      * @type Boolean
3665      */
3666     this.useShim = false;
3667     
3668     /** @private */
3669     this.shim = null;
3670     
3671     if(!existingProxy){
3672         /** @private */
3673         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3674     }else{
3675         this.proxy = Roo.get(existingProxy).dom;
3676     }
3677     /** @private */
3678     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3679     
3680     /** @private */
3681     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3682     
3683     /** @private */
3684     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3685     
3686     /** @private */
3687     this.dragSpecs = {};
3688     
3689     /**
3690      * @private The adapter to use to positon and resize elements
3691      */
3692     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3693     this.adapter.init(this);
3694     
3695     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3696         /** @private */
3697         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3698         this.el.addClass("x-splitbar-h");
3699     }else{
3700         /** @private */
3701         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3702         this.el.addClass("x-splitbar-v");
3703     }
3704     
3705     this.addEvents({
3706         /**
3707          * @event resize
3708          * Fires when the splitter is moved (alias for {@link #event-moved})
3709          * @param {Roo.SplitBar} this
3710          * @param {Number} newSize the new width or height
3711          */
3712         "resize" : true,
3713         /**
3714          * @event moved
3715          * Fires when the splitter is moved
3716          * @param {Roo.SplitBar} this
3717          * @param {Number} newSize the new width or height
3718          */
3719         "moved" : true,
3720         /**
3721          * @event beforeresize
3722          * Fires before the splitter is dragged
3723          * @param {Roo.SplitBar} this
3724          */
3725         "beforeresize" : true,
3726
3727         "beforeapply" : true
3728     });
3729
3730     Roo.util.Observable.call(this);
3731 };
3732
3733 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3734     onStartProxyDrag : function(x, y){
3735         this.fireEvent("beforeresize", this);
3736         if(!this.overlay){
3737             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3738             o.unselectable();
3739             o.enableDisplayMode("block");
3740             // all splitbars share the same overlay
3741             Roo.SplitBar.prototype.overlay = o;
3742         }
3743         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3744         this.overlay.show();
3745         Roo.get(this.proxy).setDisplayed("block");
3746         var size = this.adapter.getElementSize(this);
3747         this.activeMinSize = this.getMinimumSize();;
3748         this.activeMaxSize = this.getMaximumSize();;
3749         var c1 = size - this.activeMinSize;
3750         var c2 = Math.max(this.activeMaxSize - size, 0);
3751         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3752             this.dd.resetConstraints();
3753             this.dd.setXConstraint(
3754                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3755                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3756             );
3757             this.dd.setYConstraint(0, 0);
3758         }else{
3759             this.dd.resetConstraints();
3760             this.dd.setXConstraint(0, 0);
3761             this.dd.setYConstraint(
3762                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3763                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3764             );
3765          }
3766         this.dragSpecs.startSize = size;
3767         this.dragSpecs.startPoint = [x, y];
3768         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3769     },
3770     
3771     /** 
3772      * @private Called after the drag operation by the DDProxy
3773      */
3774     onEndProxyDrag : function(e){
3775         Roo.get(this.proxy).setDisplayed(false);
3776         var endPoint = Roo.lib.Event.getXY(e);
3777         if(this.overlay){
3778             this.overlay.hide();
3779         }
3780         var newSize;
3781         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3782             newSize = this.dragSpecs.startSize + 
3783                 (this.placement == Roo.SplitBar.LEFT ?
3784                     endPoint[0] - this.dragSpecs.startPoint[0] :
3785                     this.dragSpecs.startPoint[0] - endPoint[0]
3786                 );
3787         }else{
3788             newSize = this.dragSpecs.startSize + 
3789                 (this.placement == Roo.SplitBar.TOP ?
3790                     endPoint[1] - this.dragSpecs.startPoint[1] :
3791                     this.dragSpecs.startPoint[1] - endPoint[1]
3792                 );
3793         }
3794         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3795         if(newSize != this.dragSpecs.startSize){
3796             if(this.fireEvent('beforeapply', this, newSize) !== false){
3797                 this.adapter.setElementSize(this, newSize);
3798                 this.fireEvent("moved", this, newSize);
3799                 this.fireEvent("resize", this, newSize);
3800             }
3801         }
3802     },
3803     
3804     /**
3805      * Get the adapter this SplitBar uses
3806      * @return The adapter object
3807      */
3808     getAdapter : function(){
3809         return this.adapter;
3810     },
3811     
3812     /**
3813      * Set the adapter this SplitBar uses
3814      * @param {Object} adapter A SplitBar adapter object
3815      */
3816     setAdapter : function(adapter){
3817         this.adapter = adapter;
3818         this.adapter.init(this);
3819     },
3820     
3821     /**
3822      * Gets the minimum size for the resizing element
3823      * @return {Number} The minimum size
3824      */
3825     getMinimumSize : function(){
3826         return this.minSize;
3827     },
3828     
3829     /**
3830      * Sets the minimum size for the resizing element
3831      * @param {Number} minSize The minimum size
3832      */
3833     setMinimumSize : function(minSize){
3834         this.minSize = minSize;
3835     },
3836     
3837     /**
3838      * Gets the maximum size for the resizing element
3839      * @return {Number} The maximum size
3840      */
3841     getMaximumSize : function(){
3842         return this.maxSize;
3843     },
3844     
3845     /**
3846      * Sets the maximum size for the resizing element
3847      * @param {Number} maxSize The maximum size
3848      */
3849     setMaximumSize : function(maxSize){
3850         this.maxSize = maxSize;
3851     },
3852     
3853     /**
3854      * Sets the initialize size for the resizing element
3855      * @param {Number} size The initial size
3856      */
3857     setCurrentSize : function(size){
3858         var oldAnimate = this.animate;
3859         this.animate = false;
3860         this.adapter.setElementSize(this, size);
3861         this.animate = oldAnimate;
3862     },
3863     
3864     /**
3865      * Destroy this splitbar. 
3866      * @param {Boolean} removeEl True to remove the element
3867      */
3868     destroy : function(removeEl){
3869         if(this.shim){
3870             this.shim.remove();
3871         }
3872         this.dd.unreg();
3873         this.proxy.parentNode.removeChild(this.proxy);
3874         if(removeEl){
3875             this.el.remove();
3876         }
3877     }
3878 });
3879
3880 /**
3881  * @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.
3882  */
3883 Roo.SplitBar.createProxy = function(dir){
3884     var proxy = new Roo.Element(document.createElement("div"));
3885     proxy.unselectable();
3886     var cls = 'x-splitbar-proxy';
3887     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3888     document.body.appendChild(proxy.dom);
3889     return proxy.dom;
3890 };
3891
3892 /** 
3893  * @class Roo.SplitBar.BasicLayoutAdapter
3894  * Default Adapter. It assumes the splitter and resizing element are not positioned
3895  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3896  */
3897 Roo.SplitBar.BasicLayoutAdapter = function(){
3898 };
3899
3900 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3901     // do nothing for now
3902     init : function(s){
3903     
3904     },
3905     /**
3906      * Called before drag operations to get the current size of the resizing element. 
3907      * @param {Roo.SplitBar} s The SplitBar using this adapter
3908      */
3909      getElementSize : function(s){
3910         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3911             return s.resizingEl.getWidth();
3912         }else{
3913             return s.resizingEl.getHeight();
3914         }
3915     },
3916     
3917     /**
3918      * Called after drag operations to set the size of the resizing element.
3919      * @param {Roo.SplitBar} s The SplitBar using this adapter
3920      * @param {Number} newSize The new size to set
3921      * @param {Function} onComplete A function to be invoked when resizing is complete
3922      */
3923     setElementSize : function(s, newSize, onComplete){
3924         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3925             if(!s.animate){
3926                 s.resizingEl.setWidth(newSize);
3927                 if(onComplete){
3928                     onComplete(s, newSize);
3929                 }
3930             }else{
3931                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3932             }
3933         }else{
3934             
3935             if(!s.animate){
3936                 s.resizingEl.setHeight(newSize);
3937                 if(onComplete){
3938                     onComplete(s, newSize);
3939                 }
3940             }else{
3941                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3942             }
3943         }
3944     }
3945 };
3946
3947 /** 
3948  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3949  * @extends Roo.SplitBar.BasicLayoutAdapter
3950  * Adapter that  moves the splitter element to align with the resized sizing element. 
3951  * Used with an absolute positioned SplitBar.
3952  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3953  * document.body, make sure you assign an id to the body element.
3954  */
3955 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3956     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3957     this.container = Roo.get(container);
3958 };
3959
3960 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3961     init : function(s){
3962         this.basic.init(s);
3963     },
3964     
3965     getElementSize : function(s){
3966         return this.basic.getElementSize(s);
3967     },
3968     
3969     setElementSize : function(s, newSize, onComplete){
3970         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3971     },
3972     
3973     moveSplitter : function(s){
3974         var yes = Roo.SplitBar;
3975         switch(s.placement){
3976             case yes.LEFT:
3977                 s.el.setX(s.resizingEl.getRight());
3978                 break;
3979             case yes.RIGHT:
3980                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3981                 break;
3982             case yes.TOP:
3983                 s.el.setY(s.resizingEl.getBottom());
3984                 break;
3985             case yes.BOTTOM:
3986                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3987                 break;
3988         }
3989     }
3990 };
3991
3992 /**
3993  * Orientation constant - Create a vertical SplitBar
3994  * @static
3995  * @type Number
3996  */
3997 Roo.SplitBar.VERTICAL = 1;
3998
3999 /**
4000  * Orientation constant - Create a horizontal SplitBar
4001  * @static
4002  * @type Number
4003  */
4004 Roo.SplitBar.HORIZONTAL = 2;
4005
4006 /**
4007  * Placement constant - The resizing element is to the left of the splitter element
4008  * @static
4009  * @type Number
4010  */
4011 Roo.SplitBar.LEFT = 1;
4012
4013 /**
4014  * Placement constant - The resizing element is to the right of the splitter element
4015  * @static
4016  * @type Number
4017  */
4018 Roo.SplitBar.RIGHT = 2;
4019
4020 /**
4021  * Placement constant - The resizing element is positioned above the splitter element
4022  * @static
4023  * @type Number
4024  */
4025 Roo.SplitBar.TOP = 3;
4026
4027 /**
4028  * Placement constant - The resizing element is positioned under splitter element
4029  * @static
4030  * @type Number
4031  */
4032 Roo.SplitBar.BOTTOM = 4;
4033 /*
4034  * Based on:
4035  * Ext JS Library 1.1.1
4036  * Copyright(c) 2006-2007, Ext JS, LLC.
4037  *
4038  * Originally Released Under LGPL - original licence link has changed is not relivant.
4039  *
4040  * Fork - LGPL
4041  * <script type="text/javascript">
4042  */
4043
4044 /**
4045  * @class Roo.View
4046  * @extends Roo.util.Observable
4047  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
4048  * This class also supports single and multi selection modes. <br>
4049  * Create a data model bound view:
4050  <pre><code>
4051  var store = new Roo.data.Store(...);
4052
4053  var view = new Roo.View({
4054     el : "my-element",
4055     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
4056  
4057     singleSelect: true,
4058     selectedClass: "ydataview-selected",
4059     store: store
4060  });
4061
4062  // listen for node click?
4063  view.on("click", function(vw, index, node, e){
4064  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4065  });
4066
4067  // load XML data
4068  dataModel.load("foobar.xml");
4069  </code></pre>
4070  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4071  * <br><br>
4072  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4073  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4074  * 
4075  * Note: old style constructor is still suported (container, template, config)
4076  * 
4077  * @constructor
4078  * Create a new View
4079  * @param {Object} config The config object
4080  * 
4081  */
4082 Roo.View = function(config, depreciated_tpl, depreciated_config){
4083     
4084     this.parent = false;
4085     
4086     if (typeof(depreciated_tpl) == 'undefined') {
4087         // new way.. - universal constructor.
4088         Roo.apply(this, config);
4089         this.el  = Roo.get(this.el);
4090     } else {
4091         // old format..
4092         this.el  = Roo.get(config);
4093         this.tpl = depreciated_tpl;
4094         Roo.apply(this, depreciated_config);
4095     }
4096     this.wrapEl  = this.el.wrap().wrap();
4097     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4098     
4099     
4100     if(typeof(this.tpl) == "string"){
4101         this.tpl = new Roo.Template(this.tpl);
4102     } else {
4103         // support xtype ctors..
4104         this.tpl = new Roo.factory(this.tpl, Roo);
4105     }
4106     
4107     
4108     this.tpl.compile();
4109     
4110     /** @private */
4111     this.addEvents({
4112         /**
4113          * @event beforeclick
4114          * Fires before a click is processed. Returns false to cancel the default action.
4115          * @param {Roo.View} this
4116          * @param {Number} index The index of the target node
4117          * @param {HTMLElement} node The target node
4118          * @param {Roo.EventObject} e The raw event object
4119          */
4120             "beforeclick" : true,
4121         /**
4122          * @event click
4123          * Fires when a template node is clicked.
4124          * @param {Roo.View} this
4125          * @param {Number} index The index of the target node
4126          * @param {HTMLElement} node The target node
4127          * @param {Roo.EventObject} e The raw event object
4128          */
4129             "click" : true,
4130         /**
4131          * @event dblclick
4132          * Fires when a template node is double clicked.
4133          * @param {Roo.View} this
4134          * @param {Number} index The index of the target node
4135          * @param {HTMLElement} node The target node
4136          * @param {Roo.EventObject} e The raw event object
4137          */
4138             "dblclick" : true,
4139         /**
4140          * @event contextmenu
4141          * Fires when a template node is right clicked.
4142          * @param {Roo.View} this
4143          * @param {Number} index The index of the target node
4144          * @param {HTMLElement} node The target node
4145          * @param {Roo.EventObject} e The raw event object
4146          */
4147             "contextmenu" : true,
4148         /**
4149          * @event selectionchange
4150          * Fires when the selected nodes change.
4151          * @param {Roo.View} this
4152          * @param {Array} selections Array of the selected nodes
4153          */
4154             "selectionchange" : true,
4155     
4156         /**
4157          * @event beforeselect
4158          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4159          * @param {Roo.View} this
4160          * @param {HTMLElement} node The node to be selected
4161          * @param {Array} selections Array of currently selected nodes
4162          */
4163             "beforeselect" : true,
4164         /**
4165          * @event preparedata
4166          * Fires on every row to render, to allow you to change the data.
4167          * @param {Roo.View} this
4168          * @param {Object} data to be rendered (change this)
4169          */
4170           "preparedata" : true
4171           
4172           
4173         });
4174
4175
4176
4177     this.el.on({
4178         "click": this.onClick,
4179         "dblclick": this.onDblClick,
4180         "contextmenu": this.onContextMenu,
4181         scope:this
4182     });
4183
4184     this.selections = [];
4185     this.nodes = [];
4186     this.cmp = new Roo.CompositeElementLite([]);
4187     if(this.store){
4188         this.store = Roo.factory(this.store, Roo.data);
4189         this.setStore(this.store, true);
4190     }
4191     
4192     if ( this.footer && this.footer.xtype) {
4193            
4194          var fctr = this.wrapEl.appendChild(document.createElement("div"));
4195         
4196         this.footer.dataSource = this.store;
4197         this.footer.container = fctr;
4198         this.footer = Roo.factory(this.footer, Roo);
4199         fctr.insertFirst(this.el);
4200         
4201         // this is a bit insane - as the paging toolbar seems to detach the el..
4202 //        dom.parentNode.parentNode.parentNode
4203          // they get detached?
4204     }
4205     
4206     
4207     Roo.View.superclass.constructor.call(this);
4208     
4209     
4210 };
4211
4212 Roo.extend(Roo.View, Roo.util.Observable, {
4213     
4214      /**
4215      * @cfg {Roo.data.Store} store Data store to load data from.
4216      */
4217     store : false,
4218     
4219     /**
4220      * @cfg {String|Roo.Element} el The container element.
4221      */
4222     el : '',
4223     
4224     /**
4225      * @cfg {String|Roo.Template} tpl The template used by this View 
4226      */
4227     tpl : false,
4228     /**
4229      * @cfg {String} dataName the named area of the template to use as the data area
4230      *                          Works with domtemplates roo-name="name"
4231      */
4232     dataName: false,
4233     /**
4234      * @cfg {String} selectedClass The css class to add to selected nodes
4235      */
4236     selectedClass : "x-view-selected",
4237      /**
4238      * @cfg {String} emptyText The empty text to show when nothing is loaded.
4239      */
4240     emptyText : "",
4241     
4242     /**
4243      * @cfg {String} text to display on mask (default Loading)
4244      */
4245     mask : false,
4246     /**
4247      * @cfg {Boolean} multiSelect Allow multiple selection
4248      */
4249     multiSelect : false,
4250     /**
4251      * @cfg {Boolean} singleSelect Allow single selection
4252      */
4253     singleSelect:  false,
4254     
4255     /**
4256      * @cfg {Boolean} toggleSelect - selecting 
4257      */
4258     toggleSelect : false,
4259     
4260     /**
4261      * @cfg {Boolean} tickable - selecting 
4262      */
4263     tickable : false,
4264     
4265     /**
4266      * Returns the element this view is bound to.
4267      * @return {Roo.Element}
4268      */
4269     getEl : function(){
4270         return this.wrapEl;
4271     },
4272     
4273     
4274
4275     /**
4276      * Refreshes the view. - called by datachanged on the store. - do not call directly.
4277      */
4278     refresh : function(){
4279         //Roo.log('refresh');
4280         var t = this.tpl;
4281         
4282         // if we are using something like 'domtemplate', then
4283         // the what gets used is:
4284         // t.applySubtemplate(NAME, data, wrapping data..)
4285         // the outer template then get' applied with
4286         //     the store 'extra data'
4287         // and the body get's added to the
4288         //      roo-name="data" node?
4289         //      <span class='roo-tpl-{name}'></span> ?????
4290         
4291         
4292         
4293         this.clearSelections();
4294         this.el.update("");
4295         var html = [];
4296         var records = this.store.getRange();
4297         if(records.length < 1) {
4298             
4299             // is this valid??  = should it render a template??
4300             
4301             this.el.update(this.emptyText);
4302             return;
4303         }
4304         var el = this.el;
4305         if (this.dataName) {
4306             this.el.update(t.apply(this.store.meta)); //????
4307             el = this.el.child('.roo-tpl-' + this.dataName);
4308         }
4309         
4310         for(var i = 0, len = records.length; i < len; i++){
4311             var data = this.prepareData(records[i].data, i, records[i]);
4312             this.fireEvent("preparedata", this, data, i, records[i]);
4313             
4314             var d = Roo.apply({}, data);
4315             
4316             if(this.tickable){
4317                 Roo.apply(d, {'roo-id' : Roo.id()});
4318                 
4319                 var _this = this;
4320             
4321                 Roo.each(this.parent.item, function(item){
4322                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4323                         return;
4324                     }
4325                     Roo.apply(d, {'roo-data-checked' : 'checked'});
4326                 });
4327             }
4328             
4329             html[html.length] = Roo.util.Format.trim(
4330                 this.dataName ?
4331                     t.applySubtemplate(this.dataName, d, this.store.meta) :
4332                     t.apply(d)
4333             );
4334         }
4335         
4336         
4337         
4338         el.update(html.join(""));
4339         this.nodes = el.dom.childNodes;
4340         this.updateIndexes(0);
4341     },
4342     
4343
4344     /**
4345      * Function to override to reformat the data that is sent to
4346      * the template for each node.
4347      * DEPRICATED - use the preparedata event handler.
4348      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4349      * a JSON object for an UpdateManager bound view).
4350      */
4351     prepareData : function(data, index, record)
4352     {
4353         this.fireEvent("preparedata", this, data, index, record);
4354         return data;
4355     },
4356
4357     onUpdate : function(ds, record){
4358         // Roo.log('on update');   
4359         this.clearSelections();
4360         var index = this.store.indexOf(record);
4361         var n = this.nodes[index];
4362         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4363         n.parentNode.removeChild(n);
4364         this.updateIndexes(index, index);
4365     },
4366
4367     
4368     
4369 // --------- FIXME     
4370     onAdd : function(ds, records, index)
4371     {
4372         //Roo.log(['on Add', ds, records, index] );        
4373         this.clearSelections();
4374         if(this.nodes.length == 0){
4375             this.refresh();
4376             return;
4377         }
4378         var n = this.nodes[index];
4379         for(var i = 0, len = records.length; i < len; i++){
4380             var d = this.prepareData(records[i].data, i, records[i]);
4381             if(n){
4382                 this.tpl.insertBefore(n, d);
4383             }else{
4384                 
4385                 this.tpl.append(this.el, d);
4386             }
4387         }
4388         this.updateIndexes(index);
4389     },
4390
4391     onRemove : function(ds, record, index){
4392        // Roo.log('onRemove');
4393         this.clearSelections();
4394         var el = this.dataName  ?
4395             this.el.child('.roo-tpl-' + this.dataName) :
4396             this.el; 
4397         
4398         el.dom.removeChild(this.nodes[index]);
4399         this.updateIndexes(index);
4400     },
4401
4402     /**
4403      * Refresh an individual node.
4404      * @param {Number} index
4405      */
4406     refreshNode : function(index){
4407         this.onUpdate(this.store, this.store.getAt(index));
4408     },
4409
4410     updateIndexes : function(startIndex, endIndex){
4411         var ns = this.nodes;
4412         startIndex = startIndex || 0;
4413         endIndex = endIndex || ns.length - 1;
4414         for(var i = startIndex; i <= endIndex; i++){
4415             ns[i].nodeIndex = i;
4416         }
4417     },
4418
4419     /**
4420      * Changes the data store this view uses and refresh the view.
4421      * @param {Store} store
4422      */
4423     setStore : function(store, initial){
4424         if(!initial && this.store){
4425             this.store.un("datachanged", this.refresh);
4426             this.store.un("add", this.onAdd);
4427             this.store.un("remove", this.onRemove);
4428             this.store.un("update", this.onUpdate);
4429             this.store.un("clear", this.refresh);
4430             this.store.un("beforeload", this.onBeforeLoad);
4431             this.store.un("load", this.onLoad);
4432             this.store.un("loadexception", this.onLoad);
4433         }
4434         if(store){
4435           
4436             store.on("datachanged", this.refresh, this);
4437             store.on("add", this.onAdd, this);
4438             store.on("remove", this.onRemove, this);
4439             store.on("update", this.onUpdate, this);
4440             store.on("clear", this.refresh, this);
4441             store.on("beforeload", this.onBeforeLoad, this);
4442             store.on("load", this.onLoad, this);
4443             store.on("loadexception", this.onLoad, this);
4444         }
4445         
4446         if(store){
4447             this.refresh();
4448         }
4449     },
4450     /**
4451      * onbeforeLoad - masks the loading area.
4452      *
4453      */
4454     onBeforeLoad : function(store,opts)
4455     {
4456          //Roo.log('onBeforeLoad');   
4457         if (!opts.add) {
4458             this.el.update("");
4459         }
4460         this.el.mask(this.mask ? this.mask : "Loading" ); 
4461     },
4462     onLoad : function ()
4463     {
4464         this.el.unmask();
4465     },
4466     
4467
4468     /**
4469      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4470      * @param {HTMLElement} node
4471      * @return {HTMLElement} The template node
4472      */
4473     findItemFromChild : function(node){
4474         var el = this.dataName  ?
4475             this.el.child('.roo-tpl-' + this.dataName,true) :
4476             this.el.dom; 
4477         
4478         if(!node || node.parentNode == el){
4479                     return node;
4480             }
4481             var p = node.parentNode;
4482             while(p && p != el){
4483             if(p.parentNode == el){
4484                 return p;
4485             }
4486             p = p.parentNode;
4487         }
4488             return null;
4489     },
4490
4491     /** @ignore */
4492     onClick : function(e){
4493         var item = this.findItemFromChild(e.getTarget());
4494         if(item){
4495             var index = this.indexOf(item);
4496             if(this.onItemClick(item, index, e) !== false){
4497                 this.fireEvent("click", this, index, item, e);
4498             }
4499         }else{
4500             this.clearSelections();
4501         }
4502     },
4503
4504     /** @ignore */
4505     onContextMenu : function(e){
4506         var item = this.findItemFromChild(e.getTarget());
4507         if(item){
4508             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4509         }
4510     },
4511
4512     /** @ignore */
4513     onDblClick : function(e){
4514         var item = this.findItemFromChild(e.getTarget());
4515         if(item){
4516             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4517         }
4518     },
4519
4520     onItemClick : function(item, index, e)
4521     {
4522         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4523             return false;
4524         }
4525         if (this.toggleSelect) {
4526             var m = this.isSelected(item) ? 'unselect' : 'select';
4527             //Roo.log(m);
4528             var _t = this;
4529             _t[m](item, true, false);
4530             return true;
4531         }
4532         if(this.multiSelect || this.singleSelect){
4533             if(this.multiSelect && e.shiftKey && this.lastSelection){
4534                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4535             }else{
4536                 this.select(item, this.multiSelect && e.ctrlKey);
4537                 this.lastSelection = item;
4538             }
4539             
4540             if(!this.tickable){
4541                 e.preventDefault();
4542             }
4543             
4544         }
4545         return true;
4546     },
4547
4548     /**
4549      * Get the number of selected nodes.
4550      * @return {Number}
4551      */
4552     getSelectionCount : function(){
4553         return this.selections.length;
4554     },
4555
4556     /**
4557      * Get the currently selected nodes.
4558      * @return {Array} An array of HTMLElements
4559      */
4560     getSelectedNodes : function(){
4561         return this.selections;
4562     },
4563
4564     /**
4565      * Get the indexes of the selected nodes.
4566      * @return {Array}
4567      */
4568     getSelectedIndexes : function(){
4569         var indexes = [], s = this.selections;
4570         for(var i = 0, len = s.length; i < len; i++){
4571             indexes.push(s[i].nodeIndex);
4572         }
4573         return indexes;
4574     },
4575
4576     /**
4577      * Clear all selections
4578      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4579      */
4580     clearSelections : function(suppressEvent){
4581         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4582             this.cmp.elements = this.selections;
4583             this.cmp.removeClass(this.selectedClass);
4584             this.selections = [];
4585             if(!suppressEvent){
4586                 this.fireEvent("selectionchange", this, this.selections);
4587             }
4588         }
4589     },
4590
4591     /**
4592      * Returns true if the passed node is selected
4593      * @param {HTMLElement/Number} node The node or node index
4594      * @return {Boolean}
4595      */
4596     isSelected : function(node){
4597         var s = this.selections;
4598         if(s.length < 1){
4599             return false;
4600         }
4601         node = this.getNode(node);
4602         return s.indexOf(node) !== -1;
4603     },
4604
4605     /**
4606      * Selects nodes.
4607      * @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
4608      * @param {Boolean} keepExisting (optional) true to keep existing selections
4609      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4610      */
4611     select : function(nodeInfo, keepExisting, suppressEvent){
4612         if(nodeInfo instanceof Array){
4613             if(!keepExisting){
4614                 this.clearSelections(true);
4615             }
4616             for(var i = 0, len = nodeInfo.length; i < len; i++){
4617                 this.select(nodeInfo[i], true, true);
4618             }
4619             return;
4620         } 
4621         var node = this.getNode(nodeInfo);
4622         if(!node || this.isSelected(node)){
4623             return; // already selected.
4624         }
4625         if(!keepExisting){
4626             this.clearSelections(true);
4627         }
4628         
4629         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4630             Roo.fly(node).addClass(this.selectedClass);
4631             this.selections.push(node);
4632             if(!suppressEvent){
4633                 this.fireEvent("selectionchange", this, this.selections);
4634             }
4635         }
4636         
4637         
4638     },
4639       /**
4640      * Unselects nodes.
4641      * @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
4642      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4643      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4644      */
4645     unselect : function(nodeInfo, keepExisting, suppressEvent)
4646     {
4647         if(nodeInfo instanceof Array){
4648             Roo.each(this.selections, function(s) {
4649                 this.unselect(s, nodeInfo);
4650             }, this);
4651             return;
4652         }
4653         var node = this.getNode(nodeInfo);
4654         if(!node || !this.isSelected(node)){
4655             //Roo.log("not selected");
4656             return; // not selected.
4657         }
4658         // fireevent???
4659         var ns = [];
4660         Roo.each(this.selections, function(s) {
4661             if (s == node ) {
4662                 Roo.fly(node).removeClass(this.selectedClass);
4663
4664                 return;
4665             }
4666             ns.push(s);
4667         },this);
4668         
4669         this.selections= ns;
4670         this.fireEvent("selectionchange", this, this.selections);
4671     },
4672
4673     /**
4674      * Gets a template node.
4675      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4676      * @return {HTMLElement} The node or null if it wasn't found
4677      */
4678     getNode : function(nodeInfo){
4679         if(typeof nodeInfo == "string"){
4680             return document.getElementById(nodeInfo);
4681         }else if(typeof nodeInfo == "number"){
4682             return this.nodes[nodeInfo];
4683         }
4684         return nodeInfo;
4685     },
4686
4687     /**
4688      * Gets a range template nodes.
4689      * @param {Number} startIndex
4690      * @param {Number} endIndex
4691      * @return {Array} An array of nodes
4692      */
4693     getNodes : function(start, end){
4694         var ns = this.nodes;
4695         start = start || 0;
4696         end = typeof end == "undefined" ? ns.length - 1 : end;
4697         var nodes = [];
4698         if(start <= end){
4699             for(var i = start; i <= end; i++){
4700                 nodes.push(ns[i]);
4701             }
4702         } else{
4703             for(var i = start; i >= end; i--){
4704                 nodes.push(ns[i]);
4705             }
4706         }
4707         return nodes;
4708     },
4709
4710     /**
4711      * Finds the index of the passed node
4712      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4713      * @return {Number} The index of the node or -1
4714      */
4715     indexOf : function(node){
4716         node = this.getNode(node);
4717         if(typeof node.nodeIndex == "number"){
4718             return node.nodeIndex;
4719         }
4720         var ns = this.nodes;
4721         for(var i = 0, len = ns.length; i < len; i++){
4722             if(ns[i] == node){
4723                 return i;
4724             }
4725         }
4726         return -1;
4727     }
4728 });
4729 /*
4730  * Based on:
4731  * Ext JS Library 1.1.1
4732  * Copyright(c) 2006-2007, Ext JS, LLC.
4733  *
4734  * Originally Released Under LGPL - original licence link has changed is not relivant.
4735  *
4736  * Fork - LGPL
4737  * <script type="text/javascript">
4738  */
4739
4740 /**
4741  * @class Roo.JsonView
4742  * @extends Roo.View
4743  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4744 <pre><code>
4745 var view = new Roo.JsonView({
4746     container: "my-element",
4747     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4748     multiSelect: true, 
4749     jsonRoot: "data" 
4750 });
4751
4752 // listen for node click?
4753 view.on("click", function(vw, index, node, e){
4754     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4755 });
4756
4757 // direct load of JSON data
4758 view.load("foobar.php");
4759
4760 // Example from my blog list
4761 var tpl = new Roo.Template(
4762     '&lt;div class="entry"&gt;' +
4763     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4764     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4765     "&lt;/div&gt;&lt;hr /&gt;"
4766 );
4767
4768 var moreView = new Roo.JsonView({
4769     container :  "entry-list", 
4770     template : tpl,
4771     jsonRoot: "posts"
4772 });
4773 moreView.on("beforerender", this.sortEntries, this);
4774 moreView.load({
4775     url: "/blog/get-posts.php",
4776     params: "allposts=true",
4777     text: "Loading Blog Entries..."
4778 });
4779 </code></pre>
4780
4781 * Note: old code is supported with arguments : (container, template, config)
4782
4783
4784  * @constructor
4785  * Create a new JsonView
4786  * 
4787  * @param {Object} config The config object
4788  * 
4789  */
4790 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4791     
4792     
4793     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4794
4795     var um = this.el.getUpdateManager();
4796     um.setRenderer(this);
4797     um.on("update", this.onLoad, this);
4798     um.on("failure", this.onLoadException, this);
4799
4800     /**
4801      * @event beforerender
4802      * Fires before rendering of the downloaded JSON data.
4803      * @param {Roo.JsonView} this
4804      * @param {Object} data The JSON data loaded
4805      */
4806     /**
4807      * @event load
4808      * Fires when data is loaded.
4809      * @param {Roo.JsonView} this
4810      * @param {Object} data The JSON data loaded
4811      * @param {Object} response The raw Connect response object
4812      */
4813     /**
4814      * @event loadexception
4815      * Fires when loading fails.
4816      * @param {Roo.JsonView} this
4817      * @param {Object} response The raw Connect response object
4818      */
4819     this.addEvents({
4820         'beforerender' : true,
4821         'load' : true,
4822         'loadexception' : true
4823     });
4824 };
4825 Roo.extend(Roo.JsonView, Roo.View, {
4826     /**
4827      * @type {String} The root property in the loaded JSON object that contains the data
4828      */
4829     jsonRoot : "",
4830
4831     /**
4832      * Refreshes the view.
4833      */
4834     refresh : function(){
4835         this.clearSelections();
4836         this.el.update("");
4837         var html = [];
4838         var o = this.jsonData;
4839         if(o && o.length > 0){
4840             for(var i = 0, len = o.length; i < len; i++){
4841                 var data = this.prepareData(o[i], i, o);
4842                 html[html.length] = this.tpl.apply(data);
4843             }
4844         }else{
4845             html.push(this.emptyText);
4846         }
4847         this.el.update(html.join(""));
4848         this.nodes = this.el.dom.childNodes;
4849         this.updateIndexes(0);
4850     },
4851
4852     /**
4853      * 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.
4854      * @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:
4855      <pre><code>
4856      view.load({
4857          url: "your-url.php",
4858          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4859          callback: yourFunction,
4860          scope: yourObject, //(optional scope)
4861          discardUrl: false,
4862          nocache: false,
4863          text: "Loading...",
4864          timeout: 30,
4865          scripts: false
4866      });
4867      </code></pre>
4868      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4869      * 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.
4870      * @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}
4871      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4872      * @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.
4873      */
4874     load : function(){
4875         var um = this.el.getUpdateManager();
4876         um.update.apply(um, arguments);
4877     },
4878
4879     // note - render is a standard framework call...
4880     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4881     render : function(el, response){
4882         
4883         this.clearSelections();
4884         this.el.update("");
4885         var o;
4886         try{
4887             if (response != '') {
4888                 o = Roo.util.JSON.decode(response.responseText);
4889                 if(this.jsonRoot){
4890                     
4891                     o = o[this.jsonRoot];
4892                 }
4893             }
4894         } catch(e){
4895         }
4896         /**
4897          * The current JSON data or null
4898          */
4899         this.jsonData = o;
4900         this.beforeRender();
4901         this.refresh();
4902     },
4903
4904 /**
4905  * Get the number of records in the current JSON dataset
4906  * @return {Number}
4907  */
4908     getCount : function(){
4909         return this.jsonData ? this.jsonData.length : 0;
4910     },
4911
4912 /**
4913  * Returns the JSON object for the specified node(s)
4914  * @param {HTMLElement/Array} node The node or an array of nodes
4915  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4916  * you get the JSON object for the node
4917  */
4918     getNodeData : function(node){
4919         if(node instanceof Array){
4920             var data = [];
4921             for(var i = 0, len = node.length; i < len; i++){
4922                 data.push(this.getNodeData(node[i]));
4923             }
4924             return data;
4925         }
4926         return this.jsonData[this.indexOf(node)] || null;
4927     },
4928
4929     beforeRender : function(){
4930         this.snapshot = this.jsonData;
4931         if(this.sortInfo){
4932             this.sort.apply(this, this.sortInfo);
4933         }
4934         this.fireEvent("beforerender", this, this.jsonData);
4935     },
4936
4937     onLoad : function(el, o){
4938         this.fireEvent("load", this, this.jsonData, o);
4939     },
4940
4941     onLoadException : function(el, o){
4942         this.fireEvent("loadexception", this, o);
4943     },
4944
4945 /**
4946  * Filter the data by a specific property.
4947  * @param {String} property A property on your JSON objects
4948  * @param {String/RegExp} value Either string that the property values
4949  * should start with, or a RegExp to test against the property
4950  */
4951     filter : function(property, value){
4952         if(this.jsonData){
4953             var data = [];
4954             var ss = this.snapshot;
4955             if(typeof value == "string"){
4956                 var vlen = value.length;
4957                 if(vlen == 0){
4958                     this.clearFilter();
4959                     return;
4960                 }
4961                 value = value.toLowerCase();
4962                 for(var i = 0, len = ss.length; i < len; i++){
4963                     var o = ss[i];
4964                     if(o[property].substr(0, vlen).toLowerCase() == value){
4965                         data.push(o);
4966                     }
4967                 }
4968             } else if(value.exec){ // regex?
4969                 for(var i = 0, len = ss.length; i < len; i++){
4970                     var o = ss[i];
4971                     if(value.test(o[property])){
4972                         data.push(o);
4973                     }
4974                 }
4975             } else{
4976                 return;
4977             }
4978             this.jsonData = data;
4979             this.refresh();
4980         }
4981     },
4982
4983 /**
4984  * Filter by a function. The passed function will be called with each
4985  * object in the current dataset. If the function returns true the value is kept,
4986  * otherwise it is filtered.
4987  * @param {Function} fn
4988  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4989  */
4990     filterBy : function(fn, scope){
4991         if(this.jsonData){
4992             var data = [];
4993             var ss = this.snapshot;
4994             for(var i = 0, len = ss.length; i < len; i++){
4995                 var o = ss[i];
4996                 if(fn.call(scope || this, o)){
4997                     data.push(o);
4998                 }
4999             }
5000             this.jsonData = data;
5001             this.refresh();
5002         }
5003     },
5004
5005 /**
5006  * Clears the current filter.
5007  */
5008     clearFilter : function(){
5009         if(this.snapshot && this.jsonData != this.snapshot){
5010             this.jsonData = this.snapshot;
5011             this.refresh();
5012         }
5013     },
5014
5015
5016 /**
5017  * Sorts the data for this view and refreshes it.
5018  * @param {String} property A property on your JSON objects to sort on
5019  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
5020  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
5021  */
5022     sort : function(property, dir, sortType){
5023         this.sortInfo = Array.prototype.slice.call(arguments, 0);
5024         if(this.jsonData){
5025             var p = property;
5026             var dsc = dir && dir.toLowerCase() == "desc";
5027             var f = function(o1, o2){
5028                 var v1 = sortType ? sortType(o1[p]) : o1[p];
5029                 var v2 = sortType ? sortType(o2[p]) : o2[p];
5030                 ;
5031                 if(v1 < v2){
5032                     return dsc ? +1 : -1;
5033                 } else if(v1 > v2){
5034                     return dsc ? -1 : +1;
5035                 } else{
5036                     return 0;
5037                 }
5038             };
5039             this.jsonData.sort(f);
5040             this.refresh();
5041             if(this.jsonData != this.snapshot){
5042                 this.snapshot.sort(f);
5043             }
5044         }
5045     }
5046 });/*
5047  * Based on:
5048  * Ext JS Library 1.1.1
5049  * Copyright(c) 2006-2007, Ext JS, LLC.
5050  *
5051  * Originally Released Under LGPL - original licence link has changed is not relivant.
5052  *
5053  * Fork - LGPL
5054  * <script type="text/javascript">
5055  */
5056  
5057
5058 /**
5059  * @class Roo.ColorPalette
5060  * @extends Roo.Component
5061  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
5062  * Here's an example of typical usage:
5063  * <pre><code>
5064 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
5065 cp.render('my-div');
5066
5067 cp.on('select', function(palette, selColor){
5068     // do something with selColor
5069 });
5070 </code></pre>
5071  * @constructor
5072  * Create a new ColorPalette
5073  * @param {Object} config The config object
5074  */
5075 Roo.ColorPalette = function(config){
5076     Roo.ColorPalette.superclass.constructor.call(this, config);
5077     this.addEvents({
5078         /**
5079              * @event select
5080              * Fires when a color is selected
5081              * @param {ColorPalette} this
5082              * @param {String} color The 6-digit color hex code (without the # symbol)
5083              */
5084         select: true
5085     });
5086
5087     if(this.handler){
5088         this.on("select", this.handler, this.scope, true);
5089     }
5090 };
5091 Roo.extend(Roo.ColorPalette, Roo.Component, {
5092     /**
5093      * @cfg {String} itemCls
5094      * The CSS class to apply to the containing element (defaults to "x-color-palette")
5095      */
5096     itemCls : "x-color-palette",
5097     /**
5098      * @cfg {String} value
5099      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
5100      * the hex codes are case-sensitive.
5101      */
5102     value : null,
5103     clickEvent:'click',
5104     // private
5105     ctype: "Roo.ColorPalette",
5106
5107     /**
5108      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5109      */
5110     allowReselect : false,
5111
5112     /**
5113      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
5114      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
5115      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5116      * of colors with the width setting until the box is symmetrical.</p>
5117      * <p>You can override individual colors if needed:</p>
5118      * <pre><code>
5119 var cp = new Roo.ColorPalette();
5120 cp.colors[0] = "FF0000";  // change the first box to red
5121 </code></pre>
5122
5123 Or you can provide a custom array of your own for complete control:
5124 <pre><code>
5125 var cp = new Roo.ColorPalette();
5126 cp.colors = ["000000", "993300", "333300"];
5127 </code></pre>
5128      * @type Array
5129      */
5130     colors : [
5131         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5132         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5133         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5134         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5135         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5136     ],
5137
5138     // private
5139     onRender : function(container, position){
5140         var t = new Roo.MasterTemplate(
5141             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
5142         );
5143         var c = this.colors;
5144         for(var i = 0, len = c.length; i < len; i++){
5145             t.add([c[i]]);
5146         }
5147         var el = document.createElement("div");
5148         el.className = this.itemCls;
5149         t.overwrite(el);
5150         container.dom.insertBefore(el, position);
5151         this.el = Roo.get(el);
5152         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
5153         if(this.clickEvent != 'click'){
5154             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
5155         }
5156     },
5157
5158     // private
5159     afterRender : function(){
5160         Roo.ColorPalette.superclass.afterRender.call(this);
5161         if(this.value){
5162             var s = this.value;
5163             this.value = null;
5164             this.select(s);
5165         }
5166     },
5167
5168     // private
5169     handleClick : function(e, t){
5170         e.preventDefault();
5171         if(!this.disabled){
5172             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5173             this.select(c.toUpperCase());
5174         }
5175     },
5176
5177     /**
5178      * Selects the specified color in the palette (fires the select event)
5179      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5180      */
5181     select : function(color){
5182         color = color.replace("#", "");
5183         if(color != this.value || this.allowReselect){
5184             var el = this.el;
5185             if(this.value){
5186                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5187             }
5188             el.child("a.color-"+color).addClass("x-color-palette-sel");
5189             this.value = color;
5190             this.fireEvent("select", this, color);
5191         }
5192     }
5193 });/*
5194  * Based on:
5195  * Ext JS Library 1.1.1
5196  * Copyright(c) 2006-2007, Ext JS, LLC.
5197  *
5198  * Originally Released Under LGPL - original licence link has changed is not relivant.
5199  *
5200  * Fork - LGPL
5201  * <script type="text/javascript">
5202  */
5203  
5204 /**
5205  * @class Roo.DatePicker
5206  * @extends Roo.Component
5207  * Simple date picker class.
5208  * @constructor
5209  * Create a new DatePicker
5210  * @param {Object} config The config object
5211  */
5212 Roo.DatePicker = function(config){
5213     Roo.DatePicker.superclass.constructor.call(this, config);
5214
5215     this.value = config && config.value ?
5216                  config.value.clearTime() : new Date().clearTime();
5217
5218     this.addEvents({
5219         /**
5220              * @event select
5221              * Fires when a date is selected
5222              * @param {DatePicker} this
5223              * @param {Date} date The selected date
5224              */
5225         'select': true,
5226         /**
5227              * @event monthchange
5228              * Fires when the displayed month changes 
5229              * @param {DatePicker} this
5230              * @param {Date} date The selected month
5231              */
5232         'monthchange': true
5233     });
5234
5235     if(this.handler){
5236         this.on("select", this.handler,  this.scope || this);
5237     }
5238     // build the disabledDatesRE
5239     if(!this.disabledDatesRE && this.disabledDates){
5240         var dd = this.disabledDates;
5241         var re = "(?:";
5242         for(var i = 0; i < dd.length; i++){
5243             re += dd[i];
5244             if(i != dd.length-1) {
5245                 re += "|";
5246             }
5247         }
5248         this.disabledDatesRE = new RegExp(re + ")");
5249     }
5250 };
5251
5252 Roo.extend(Roo.DatePicker, Roo.Component, {
5253     /**
5254      * @cfg {String} todayText
5255      * The text to display on the button that selects the current date (defaults to "Today")
5256      */
5257     todayText : "Today",
5258     /**
5259      * @cfg {String} okText
5260      * The text to display on the ok button
5261      */
5262     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
5263     /**
5264      * @cfg {String} cancelText
5265      * The text to display on the cancel button
5266      */
5267     cancelText : "Cancel",
5268     /**
5269      * @cfg {String} todayTip
5270      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5271      */
5272     todayTip : "{0} (Spacebar)",
5273     /**
5274      * @cfg {Date} minDate
5275      * Minimum allowable date (JavaScript date object, defaults to null)
5276      */
5277     minDate : null,
5278     /**
5279      * @cfg {Date} maxDate
5280      * Maximum allowable date (JavaScript date object, defaults to null)
5281      */
5282     maxDate : null,
5283     /**
5284      * @cfg {String} minText
5285      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5286      */
5287     minText : "This date is before the minimum date",
5288     /**
5289      * @cfg {String} maxText
5290      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5291      */
5292     maxText : "This date is after the maximum date",
5293     /**
5294      * @cfg {String} format
5295      * The default date format string which can be overriden for localization support.  The format must be
5296      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5297      */
5298     format : "m/d/y",
5299     /**
5300      * @cfg {Array} disabledDays
5301      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5302      */
5303     disabledDays : null,
5304     /**
5305      * @cfg {String} disabledDaysText
5306      * The tooltip to display when the date falls on a disabled day (defaults to "")
5307      */
5308     disabledDaysText : "",
5309     /**
5310      * @cfg {RegExp} disabledDatesRE
5311      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5312      */
5313     disabledDatesRE : null,
5314     /**
5315      * @cfg {String} disabledDatesText
5316      * The tooltip text to display when the date falls on a disabled date (defaults to "")
5317      */
5318     disabledDatesText : "",
5319     /**
5320      * @cfg {Boolean} constrainToViewport
5321      * True to constrain the date picker to the viewport (defaults to true)
5322      */
5323     constrainToViewport : true,
5324     /**
5325      * @cfg {Array} monthNames
5326      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5327      */
5328     monthNames : Date.monthNames,
5329     /**
5330      * @cfg {Array} dayNames
5331      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5332      */
5333     dayNames : Date.dayNames,
5334     /**
5335      * @cfg {String} nextText
5336      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5337      */
5338     nextText: 'Next Month (Control+Right)',
5339     /**
5340      * @cfg {String} prevText
5341      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5342      */
5343     prevText: 'Previous Month (Control+Left)',
5344     /**
5345      * @cfg {String} monthYearText
5346      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5347      */
5348     monthYearText: 'Choose a month (Control+Up/Down to move years)',
5349     /**
5350      * @cfg {Number} startDay
5351      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5352      */
5353     startDay : 0,
5354     /**
5355      * @cfg {Bool} showClear
5356      * Show a clear button (usefull for date form elements that can be blank.)
5357      */
5358     
5359     showClear: false,
5360     
5361     /**
5362      * Sets the value of the date field
5363      * @param {Date} value The date to set
5364      */
5365     setValue : function(value){
5366         var old = this.value;
5367         
5368         if (typeof(value) == 'string') {
5369          
5370             value = Date.parseDate(value, this.format);
5371         }
5372         if (!value) {
5373             value = new Date();
5374         }
5375         
5376         this.value = value.clearTime(true);
5377         if(this.el){
5378             this.update(this.value);
5379         }
5380     },
5381
5382     /**
5383      * Gets the current selected value of the date field
5384      * @return {Date} The selected date
5385      */
5386     getValue : function(){
5387         return this.value;
5388     },
5389
5390     // private
5391     focus : function(){
5392         if(this.el){
5393             this.update(this.activeDate);
5394         }
5395     },
5396
5397     // privateval
5398     onRender : function(container, position){
5399         
5400         var m = [
5401              '<table cellspacing="0">',
5402                 '<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>',
5403                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5404         var dn = this.dayNames;
5405         for(var i = 0; i < 7; i++){
5406             var d = this.startDay+i;
5407             if(d > 6){
5408                 d = d-7;
5409             }
5410             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5411         }
5412         m[m.length] = "</tr></thead><tbody><tr>";
5413         for(var i = 0; i < 42; i++) {
5414             if(i % 7 == 0 && i != 0){
5415                 m[m.length] = "</tr><tr>";
5416             }
5417             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5418         }
5419         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5420             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5421
5422         var el = document.createElement("div");
5423         el.className = "x-date-picker";
5424         el.innerHTML = m.join("");
5425
5426         container.dom.insertBefore(el, position);
5427
5428         this.el = Roo.get(el);
5429         this.eventEl = Roo.get(el.firstChild);
5430
5431         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5432             handler: this.showPrevMonth,
5433             scope: this,
5434             preventDefault:true,
5435             stopDefault:true
5436         });
5437
5438         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5439             handler: this.showNextMonth,
5440             scope: this,
5441             preventDefault:true,
5442             stopDefault:true
5443         });
5444
5445         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5446
5447         this.monthPicker = this.el.down('div.x-date-mp');
5448         this.monthPicker.enableDisplayMode('block');
5449         
5450         var kn = new Roo.KeyNav(this.eventEl, {
5451             "left" : function(e){
5452                 e.ctrlKey ?
5453                     this.showPrevMonth() :
5454                     this.update(this.activeDate.add("d", -1));
5455             },
5456
5457             "right" : function(e){
5458                 e.ctrlKey ?
5459                     this.showNextMonth() :
5460                     this.update(this.activeDate.add("d", 1));
5461             },
5462
5463             "up" : function(e){
5464                 e.ctrlKey ?
5465                     this.showNextYear() :
5466                     this.update(this.activeDate.add("d", -7));
5467             },
5468
5469             "down" : function(e){
5470                 e.ctrlKey ?
5471                     this.showPrevYear() :
5472                     this.update(this.activeDate.add("d", 7));
5473             },
5474
5475             "pageUp" : function(e){
5476                 this.showNextMonth();
5477             },
5478
5479             "pageDown" : function(e){
5480                 this.showPrevMonth();
5481             },
5482
5483             "enter" : function(e){
5484                 e.stopPropagation();
5485                 return true;
5486             },
5487
5488             scope : this
5489         });
5490
5491         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5492
5493         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5494
5495         this.el.unselectable();
5496         
5497         this.cells = this.el.select("table.x-date-inner tbody td");
5498         this.textNodes = this.el.query("table.x-date-inner tbody span");
5499
5500         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5501             text: "&#160;",
5502             tooltip: this.monthYearText
5503         });
5504
5505         this.mbtn.on('click', this.showMonthPicker, this);
5506         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5507
5508
5509         var today = (new Date()).dateFormat(this.format);
5510         
5511         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5512         if (this.showClear) {
5513             baseTb.add( new Roo.Toolbar.Fill());
5514         }
5515         baseTb.add({
5516             text: String.format(this.todayText, today),
5517             tooltip: String.format(this.todayTip, today),
5518             handler: this.selectToday,
5519             scope: this
5520         });
5521         
5522         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5523             
5524         //});
5525         if (this.showClear) {
5526             
5527             baseTb.add( new Roo.Toolbar.Fill());
5528             baseTb.add({
5529                 text: '&#160;',
5530                 cls: 'x-btn-icon x-btn-clear',
5531                 handler: function() {
5532                     //this.value = '';
5533                     this.fireEvent("select", this, '');
5534                 },
5535                 scope: this
5536             });
5537         }
5538         
5539         
5540         if(Roo.isIE){
5541             this.el.repaint();
5542         }
5543         this.update(this.value);
5544     },
5545
5546     createMonthPicker : function(){
5547         if(!this.monthPicker.dom.firstChild){
5548             var buf = ['<table border="0" cellspacing="0">'];
5549             for(var i = 0; i < 6; i++){
5550                 buf.push(
5551                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5552                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5553                     i == 0 ?
5554                     '<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>' :
5555                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5556                 );
5557             }
5558             buf.push(
5559                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5560                     this.okText,
5561                     '</button><button type="button" class="x-date-mp-cancel">',
5562                     this.cancelText,
5563                     '</button></td></tr>',
5564                 '</table>'
5565             );
5566             this.monthPicker.update(buf.join(''));
5567             this.monthPicker.on('click', this.onMonthClick, this);
5568             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5569
5570             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5571             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5572
5573             this.mpMonths.each(function(m, a, i){
5574                 i += 1;
5575                 if((i%2) == 0){
5576                     m.dom.xmonth = 5 + Math.round(i * .5);
5577                 }else{
5578                     m.dom.xmonth = Math.round((i-1) * .5);
5579                 }
5580             });
5581         }
5582     },
5583
5584     showMonthPicker : function(){
5585         this.createMonthPicker();
5586         var size = this.el.getSize();
5587         this.monthPicker.setSize(size);
5588         this.monthPicker.child('table').setSize(size);
5589
5590         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5591         this.updateMPMonth(this.mpSelMonth);
5592         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5593         this.updateMPYear(this.mpSelYear);
5594
5595         this.monthPicker.slideIn('t', {duration:.2});
5596     },
5597
5598     updateMPYear : function(y){
5599         this.mpyear = y;
5600         var ys = this.mpYears.elements;
5601         for(var i = 1; i <= 10; i++){
5602             var td = ys[i-1], y2;
5603             if((i%2) == 0){
5604                 y2 = y + Math.round(i * .5);
5605                 td.firstChild.innerHTML = y2;
5606                 td.xyear = y2;
5607             }else{
5608                 y2 = y - (5-Math.round(i * .5));
5609                 td.firstChild.innerHTML = y2;
5610                 td.xyear = y2;
5611             }
5612             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5613         }
5614     },
5615
5616     updateMPMonth : function(sm){
5617         this.mpMonths.each(function(m, a, i){
5618             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5619         });
5620     },
5621
5622     selectMPMonth: function(m){
5623         
5624     },
5625
5626     onMonthClick : function(e, t){
5627         e.stopEvent();
5628         var el = new Roo.Element(t), pn;
5629         if(el.is('button.x-date-mp-cancel')){
5630             this.hideMonthPicker();
5631         }
5632         else if(el.is('button.x-date-mp-ok')){
5633             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5634             this.hideMonthPicker();
5635         }
5636         else if(pn = el.up('td.x-date-mp-month', 2)){
5637             this.mpMonths.removeClass('x-date-mp-sel');
5638             pn.addClass('x-date-mp-sel');
5639             this.mpSelMonth = pn.dom.xmonth;
5640         }
5641         else if(pn = el.up('td.x-date-mp-year', 2)){
5642             this.mpYears.removeClass('x-date-mp-sel');
5643             pn.addClass('x-date-mp-sel');
5644             this.mpSelYear = pn.dom.xyear;
5645         }
5646         else if(el.is('a.x-date-mp-prev')){
5647             this.updateMPYear(this.mpyear-10);
5648         }
5649         else if(el.is('a.x-date-mp-next')){
5650             this.updateMPYear(this.mpyear+10);
5651         }
5652     },
5653
5654     onMonthDblClick : function(e, t){
5655         e.stopEvent();
5656         var el = new Roo.Element(t), pn;
5657         if(pn = el.up('td.x-date-mp-month', 2)){
5658             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5659             this.hideMonthPicker();
5660         }
5661         else if(pn = el.up('td.x-date-mp-year', 2)){
5662             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5663             this.hideMonthPicker();
5664         }
5665     },
5666
5667     hideMonthPicker : function(disableAnim){
5668         if(this.monthPicker){
5669             if(disableAnim === true){
5670                 this.monthPicker.hide();
5671             }else{
5672                 this.monthPicker.slideOut('t', {duration:.2});
5673             }
5674         }
5675     },
5676
5677     // private
5678     showPrevMonth : function(e){
5679         this.update(this.activeDate.add("mo", -1));
5680     },
5681
5682     // private
5683     showNextMonth : function(e){
5684         this.update(this.activeDate.add("mo", 1));
5685     },
5686
5687     // private
5688     showPrevYear : function(){
5689         this.update(this.activeDate.add("y", -1));
5690     },
5691
5692     // private
5693     showNextYear : function(){
5694         this.update(this.activeDate.add("y", 1));
5695     },
5696
5697     // private
5698     handleMouseWheel : function(e){
5699         var delta = e.getWheelDelta();
5700         if(delta > 0){
5701             this.showPrevMonth();
5702             e.stopEvent();
5703         } else if(delta < 0){
5704             this.showNextMonth();
5705             e.stopEvent();
5706         }
5707     },
5708
5709     // private
5710     handleDateClick : function(e, t){
5711         e.stopEvent();
5712         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5713             this.setValue(new Date(t.dateValue));
5714             this.fireEvent("select", this, this.value);
5715         }
5716     },
5717
5718     // private
5719     selectToday : function(){
5720         this.setValue(new Date().clearTime());
5721         this.fireEvent("select", this, this.value);
5722     },
5723
5724     // private
5725     update : function(date)
5726     {
5727         var vd = this.activeDate;
5728         this.activeDate = date;
5729         if(vd && this.el){
5730             var t = date.getTime();
5731             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5732                 this.cells.removeClass("x-date-selected");
5733                 this.cells.each(function(c){
5734                    if(c.dom.firstChild.dateValue == t){
5735                        c.addClass("x-date-selected");
5736                        setTimeout(function(){
5737                             try{c.dom.firstChild.focus();}catch(e){}
5738                        }, 50);
5739                        return false;
5740                    }
5741                 });
5742                 return;
5743             }
5744         }
5745         
5746         var days = date.getDaysInMonth();
5747         var firstOfMonth = date.getFirstDateOfMonth();
5748         var startingPos = firstOfMonth.getDay()-this.startDay;
5749
5750         if(startingPos <= this.startDay){
5751             startingPos += 7;
5752         }
5753
5754         var pm = date.add("mo", -1);
5755         var prevStart = pm.getDaysInMonth()-startingPos;
5756
5757         var cells = this.cells.elements;
5758         var textEls = this.textNodes;
5759         days += startingPos;
5760
5761         // convert everything to numbers so it's fast
5762         var day = 86400000;
5763         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5764         var today = new Date().clearTime().getTime();
5765         var sel = date.clearTime().getTime();
5766         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5767         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5768         var ddMatch = this.disabledDatesRE;
5769         var ddText = this.disabledDatesText;
5770         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5771         var ddaysText = this.disabledDaysText;
5772         var format = this.format;
5773
5774         var setCellClass = function(cal, cell){
5775             cell.title = "";
5776             var t = d.getTime();
5777             cell.firstChild.dateValue = t;
5778             if(t == today){
5779                 cell.className += " x-date-today";
5780                 cell.title = cal.todayText;
5781             }
5782             if(t == sel){
5783                 cell.className += " x-date-selected";
5784                 setTimeout(function(){
5785                     try{cell.firstChild.focus();}catch(e){}
5786                 }, 50);
5787             }
5788             // disabling
5789             if(t < min) {
5790                 cell.className = " x-date-disabled";
5791                 cell.title = cal.minText;
5792                 return;
5793             }
5794             if(t > max) {
5795                 cell.className = " x-date-disabled";
5796                 cell.title = cal.maxText;
5797                 return;
5798             }
5799             if(ddays){
5800                 if(ddays.indexOf(d.getDay()) != -1){
5801                     cell.title = ddaysText;
5802                     cell.className = " x-date-disabled";
5803                 }
5804             }
5805             if(ddMatch && format){
5806                 var fvalue = d.dateFormat(format);
5807                 if(ddMatch.test(fvalue)){
5808                     cell.title = ddText.replace("%0", fvalue);
5809                     cell.className = " x-date-disabled";
5810                 }
5811             }
5812         };
5813
5814         var i = 0;
5815         for(; i < startingPos; i++) {
5816             textEls[i].innerHTML = (++prevStart);
5817             d.setDate(d.getDate()+1);
5818             cells[i].className = "x-date-prevday";
5819             setCellClass(this, cells[i]);
5820         }
5821         for(; i < days; i++){
5822             intDay = i - startingPos + 1;
5823             textEls[i].innerHTML = (intDay);
5824             d.setDate(d.getDate()+1);
5825             cells[i].className = "x-date-active";
5826             setCellClass(this, cells[i]);
5827         }
5828         var extraDays = 0;
5829         for(; i < 42; i++) {
5830              textEls[i].innerHTML = (++extraDays);
5831              d.setDate(d.getDate()+1);
5832              cells[i].className = "x-date-nextday";
5833              setCellClass(this, cells[i]);
5834         }
5835
5836         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5837         this.fireEvent('monthchange', this, date);
5838         
5839         if(!this.internalRender){
5840             var main = this.el.dom.firstChild;
5841             var w = main.offsetWidth;
5842             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5843             Roo.fly(main).setWidth(w);
5844             this.internalRender = true;
5845             // opera does not respect the auto grow header center column
5846             // then, after it gets a width opera refuses to recalculate
5847             // without a second pass
5848             if(Roo.isOpera && !this.secondPass){
5849                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5850                 this.secondPass = true;
5851                 this.update.defer(10, this, [date]);
5852             }
5853         }
5854         
5855         
5856     }
5857 });        /*
5858  * Based on:
5859  * Ext JS Library 1.1.1
5860  * Copyright(c) 2006-2007, Ext JS, LLC.
5861  *
5862  * Originally Released Under LGPL - original licence link has changed is not relivant.
5863  *
5864  * Fork - LGPL
5865  * <script type="text/javascript">
5866  */
5867 /**
5868  * @class Roo.TabPanel
5869  * @extends Roo.util.Observable
5870  * A lightweight tab container.
5871  * <br><br>
5872  * Usage:
5873  * <pre><code>
5874 // basic tabs 1, built from existing content
5875 var tabs = new Roo.TabPanel("tabs1");
5876 tabs.addTab("script", "View Script");
5877 tabs.addTab("markup", "View Markup");
5878 tabs.activate("script");
5879
5880 // more advanced tabs, built from javascript
5881 var jtabs = new Roo.TabPanel("jtabs");
5882 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5883
5884 // set up the UpdateManager
5885 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5886 var updater = tab2.getUpdateManager();
5887 updater.setDefaultUrl("ajax1.htm");
5888 tab2.on('activate', updater.refresh, updater, true);
5889
5890 // Use setUrl for Ajax loading
5891 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5892 tab3.setUrl("ajax2.htm", null, true);
5893
5894 // Disabled tab
5895 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5896 tab4.disable();
5897
5898 jtabs.activate("jtabs-1");
5899  * </code></pre>
5900  * @constructor
5901  * Create a new TabPanel.
5902  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5903  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5904  */
5905 Roo.TabPanel = function(container, config){
5906     /**
5907     * The container element for this TabPanel.
5908     * @type Roo.Element
5909     */
5910     this.el = Roo.get(container, true);
5911     if(config){
5912         if(typeof config == "boolean"){
5913             this.tabPosition = config ? "bottom" : "top";
5914         }else{
5915             Roo.apply(this, config);
5916         }
5917     }
5918     if(this.tabPosition == "bottom"){
5919         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5920         this.el.addClass("x-tabs-bottom");
5921     }
5922     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5923     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5924     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5925     if(Roo.isIE){
5926         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5927     }
5928     if(this.tabPosition != "bottom"){
5929         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5930          * @type Roo.Element
5931          */
5932         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5933         this.el.addClass("x-tabs-top");
5934     }
5935     this.items = [];
5936
5937     this.bodyEl.setStyle("position", "relative");
5938
5939     this.active = null;
5940     this.activateDelegate = this.activate.createDelegate(this);
5941
5942     this.addEvents({
5943         /**
5944          * @event tabchange
5945          * Fires when the active tab changes
5946          * @param {Roo.TabPanel} this
5947          * @param {Roo.TabPanelItem} activePanel The new active tab
5948          */
5949         "tabchange": true,
5950         /**
5951          * @event beforetabchange
5952          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5953          * @param {Roo.TabPanel} this
5954          * @param {Object} e Set cancel to true on this object to cancel the tab change
5955          * @param {Roo.TabPanelItem} tab The tab being changed to
5956          */
5957         "beforetabchange" : true
5958     });
5959
5960     Roo.EventManager.onWindowResize(this.onResize, this);
5961     this.cpad = this.el.getPadding("lr");
5962     this.hiddenCount = 0;
5963
5964
5965     // toolbar on the tabbar support...
5966     if (this.toolbar) {
5967         var tcfg = this.toolbar;
5968         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5969         this.toolbar = new Roo.Toolbar(tcfg);
5970         if (Roo.isSafari) {
5971             var tbl = tcfg.container.child('table', true);
5972             tbl.setAttribute('width', '100%');
5973         }
5974         
5975     }
5976    
5977
5978
5979     Roo.TabPanel.superclass.constructor.call(this);
5980 };
5981
5982 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5983     /*
5984      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5985      */
5986     tabPosition : "top",
5987     /*
5988      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5989      */
5990     currentTabWidth : 0,
5991     /*
5992      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5993      */
5994     minTabWidth : 40,
5995     /*
5996      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5997      */
5998     maxTabWidth : 250,
5999     /*
6000      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
6001      */
6002     preferredTabWidth : 175,
6003     /*
6004      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
6005      */
6006     resizeTabs : false,
6007     /*
6008      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
6009      */
6010     monitorResize : true,
6011     /*
6012      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
6013      */
6014     toolbar : false,
6015
6016     /**
6017      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
6018      * @param {String} id The id of the div to use <b>or create</b>
6019      * @param {String} text The text for the tab
6020      * @param {String} content (optional) Content to put in the TabPanelItem body
6021      * @param {Boolean} closable (optional) True to create a close icon on the tab
6022      * @return {Roo.TabPanelItem} The created TabPanelItem
6023      */
6024     addTab : function(id, text, content, closable){
6025         var item = new Roo.TabPanelItem(this, id, text, closable);
6026         this.addTabItem(item);
6027         if(content){
6028             item.setContent(content);
6029         }
6030         return item;
6031     },
6032
6033     /**
6034      * Returns the {@link Roo.TabPanelItem} with the specified id/index
6035      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6036      * @return {Roo.TabPanelItem}
6037      */
6038     getTab : function(id){
6039         return this.items[id];
6040     },
6041
6042     /**
6043      * Hides the {@link Roo.TabPanelItem} with the specified id/index
6044      * @param {String/Number} id The id or index of the TabPanelItem to hide.
6045      */
6046     hideTab : function(id){
6047         var t = this.items[id];
6048         if(!t.isHidden()){
6049            t.setHidden(true);
6050            this.hiddenCount++;
6051            this.autoSizeTabs();
6052         }
6053     },
6054
6055     /**
6056      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6057      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6058      */
6059     unhideTab : function(id){
6060         var t = this.items[id];
6061         if(t.isHidden()){
6062            t.setHidden(false);
6063            this.hiddenCount--;
6064            this.autoSizeTabs();
6065         }
6066     },
6067
6068     /**
6069      * Adds an existing {@link Roo.TabPanelItem}.
6070      * @param {Roo.TabPanelItem} item The TabPanelItem to add
6071      */
6072     addTabItem : function(item){
6073         this.items[item.id] = item;
6074         this.items.push(item);
6075         if(this.resizeTabs){
6076            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6077            this.autoSizeTabs();
6078         }else{
6079             item.autoSize();
6080         }
6081     },
6082
6083     /**
6084      * Removes a {@link Roo.TabPanelItem}.
6085      * @param {String/Number} id The id or index of the TabPanelItem to remove.
6086      */
6087     removeTab : function(id){
6088         var items = this.items;
6089         var tab = items[id];
6090         if(!tab) { return; }
6091         var index = items.indexOf(tab);
6092         if(this.active == tab && items.length > 1){
6093             var newTab = this.getNextAvailable(index);
6094             if(newTab) {
6095                 newTab.activate();
6096             }
6097         }
6098         this.stripEl.dom.removeChild(tab.pnode.dom);
6099         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6100             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6101         }
6102         items.splice(index, 1);
6103         delete this.items[tab.id];
6104         tab.fireEvent("close", tab);
6105         tab.purgeListeners();
6106         this.autoSizeTabs();
6107     },
6108
6109     getNextAvailable : function(start){
6110         var items = this.items;
6111         var index = start;
6112         // look for a next tab that will slide over to
6113         // replace the one being removed
6114         while(index < items.length){
6115             var item = items[++index];
6116             if(item && !item.isHidden()){
6117                 return item;
6118             }
6119         }
6120         // if one isn't found select the previous tab (on the left)
6121         index = start;
6122         while(index >= 0){
6123             var item = items[--index];
6124             if(item && !item.isHidden()){
6125                 return item;
6126             }
6127         }
6128         return null;
6129     },
6130
6131     /**
6132      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6133      * @param {String/Number} id The id or index of the TabPanelItem to disable.
6134      */
6135     disableTab : function(id){
6136         var tab = this.items[id];
6137         if(tab && this.active != tab){
6138             tab.disable();
6139         }
6140     },
6141
6142     /**
6143      * Enables a {@link Roo.TabPanelItem} that is disabled.
6144      * @param {String/Number} id The id or index of the TabPanelItem to enable.
6145      */
6146     enableTab : function(id){
6147         var tab = this.items[id];
6148         tab.enable();
6149     },
6150
6151     /**
6152      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6153      * @param {String/Number} id The id or index of the TabPanelItem to activate.
6154      * @return {Roo.TabPanelItem} The TabPanelItem.
6155      */
6156     activate : function(id){
6157         var tab = this.items[id];
6158         if(!tab){
6159             return null;
6160         }
6161         if(tab == this.active || tab.disabled){
6162             return tab;
6163         }
6164         var e = {};
6165         this.fireEvent("beforetabchange", this, e, tab);
6166         if(e.cancel !== true && !tab.disabled){
6167             if(this.active){
6168                 this.active.hide();
6169             }
6170             this.active = this.items[id];
6171             this.active.show();
6172             this.fireEvent("tabchange", this, this.active);
6173         }
6174         return tab;
6175     },
6176
6177     /**
6178      * Gets the active {@link Roo.TabPanelItem}.
6179      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6180      */
6181     getActiveTab : function(){
6182         return this.active;
6183     },
6184
6185     /**
6186      * Updates the tab body element to fit the height of the container element
6187      * for overflow scrolling
6188      * @param {Number} targetHeight (optional) Override the starting height from the elements height
6189      */
6190     syncHeight : function(targetHeight){
6191         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6192         var bm = this.bodyEl.getMargins();
6193         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6194         this.bodyEl.setHeight(newHeight);
6195         return newHeight;
6196     },
6197
6198     onResize : function(){
6199         if(this.monitorResize){
6200             this.autoSizeTabs();
6201         }
6202     },
6203
6204     /**
6205      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6206      */
6207     beginUpdate : function(){
6208         this.updating = true;
6209     },
6210
6211     /**
6212      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6213      */
6214     endUpdate : function(){
6215         this.updating = false;
6216         this.autoSizeTabs();
6217     },
6218
6219     /**
6220      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6221      */
6222     autoSizeTabs : function(){
6223         var count = this.items.length;
6224         var vcount = count - this.hiddenCount;
6225         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6226             return;
6227         }
6228         var w = Math.max(this.el.getWidth() - this.cpad, 10);
6229         var availWidth = Math.floor(w / vcount);
6230         var b = this.stripBody;
6231         if(b.getWidth() > w){
6232             var tabs = this.items;
6233             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6234             if(availWidth < this.minTabWidth){
6235                 /*if(!this.sleft){    // incomplete scrolling code
6236                     this.createScrollButtons();
6237                 }
6238                 this.showScroll();
6239                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6240             }
6241         }else{
6242             if(this.currentTabWidth < this.preferredTabWidth){
6243                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6244             }
6245         }
6246     },
6247
6248     /**
6249      * Returns the number of tabs in this TabPanel.
6250      * @return {Number}
6251      */
6252      getCount : function(){
6253          return this.items.length;
6254      },
6255
6256     /**
6257      * Resizes all the tabs to the passed width
6258      * @param {Number} The new width
6259      */
6260     setTabWidth : function(width){
6261         this.currentTabWidth = width;
6262         for(var i = 0, len = this.items.length; i < len; i++) {
6263                 if(!this.items[i].isHidden()) {
6264                 this.items[i].setWidth(width);
6265             }
6266         }
6267     },
6268
6269     /**
6270      * Destroys this TabPanel
6271      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6272      */
6273     destroy : function(removeEl){
6274         Roo.EventManager.removeResizeListener(this.onResize, this);
6275         for(var i = 0, len = this.items.length; i < len; i++){
6276             this.items[i].purgeListeners();
6277         }
6278         if(removeEl === true){
6279             this.el.update("");
6280             this.el.remove();
6281         }
6282     }
6283 });
6284
6285 /**
6286  * @class Roo.TabPanelItem
6287  * @extends Roo.util.Observable
6288  * Represents an individual item (tab plus body) in a TabPanel.
6289  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6290  * @param {String} id The id of this TabPanelItem
6291  * @param {String} text The text for the tab of this TabPanelItem
6292  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6293  */
6294 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6295     /**
6296      * The {@link Roo.TabPanel} this TabPanelItem belongs to
6297      * @type Roo.TabPanel
6298      */
6299     this.tabPanel = tabPanel;
6300     /**
6301      * The id for this TabPanelItem
6302      * @type String
6303      */
6304     this.id = id;
6305     /** @private */
6306     this.disabled = false;
6307     /** @private */
6308     this.text = text;
6309     /** @private */
6310     this.loaded = false;
6311     this.closable = closable;
6312
6313     /**
6314      * The body element for this TabPanelItem.
6315      * @type Roo.Element
6316      */
6317     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6318     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6319     this.bodyEl.setStyle("display", "block");
6320     this.bodyEl.setStyle("zoom", "1");
6321     this.hideAction();
6322
6323     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6324     /** @private */
6325     this.el = Roo.get(els.el, true);
6326     this.inner = Roo.get(els.inner, true);
6327     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6328     this.pnode = Roo.get(els.el.parentNode, true);
6329     this.el.on("mousedown", this.onTabMouseDown, this);
6330     this.el.on("click", this.onTabClick, this);
6331     /** @private */
6332     if(closable){
6333         var c = Roo.get(els.close, true);
6334         c.dom.title = this.closeText;
6335         c.addClassOnOver("close-over");
6336         c.on("click", this.closeClick, this);
6337      }
6338
6339     this.addEvents({
6340          /**
6341          * @event activate
6342          * Fires when this tab becomes the active tab.
6343          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6344          * @param {Roo.TabPanelItem} this
6345          */
6346         "activate": true,
6347         /**
6348          * @event beforeclose
6349          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6350          * @param {Roo.TabPanelItem} this
6351          * @param {Object} e Set cancel to true on this object to cancel the close.
6352          */
6353         "beforeclose": true,
6354         /**
6355          * @event close
6356          * Fires when this tab is closed.
6357          * @param {Roo.TabPanelItem} this
6358          */
6359          "close": true,
6360         /**
6361          * @event deactivate
6362          * Fires when this tab is no longer the active tab.
6363          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6364          * @param {Roo.TabPanelItem} this
6365          */
6366          "deactivate" : true
6367     });
6368     this.hidden = false;
6369
6370     Roo.TabPanelItem.superclass.constructor.call(this);
6371 };
6372
6373 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6374     purgeListeners : function(){
6375        Roo.util.Observable.prototype.purgeListeners.call(this);
6376        this.el.removeAllListeners();
6377     },
6378     /**
6379      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6380      */
6381     show : function(){
6382         this.pnode.addClass("on");
6383         this.showAction();
6384         if(Roo.isOpera){
6385             this.tabPanel.stripWrap.repaint();
6386         }
6387         this.fireEvent("activate", this.tabPanel, this);
6388     },
6389
6390     /**
6391      * Returns true if this tab is the active tab.
6392      * @return {Boolean}
6393      */
6394     isActive : function(){
6395         return this.tabPanel.getActiveTab() == this;
6396     },
6397
6398     /**
6399      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6400      */
6401     hide : function(){
6402         this.pnode.removeClass("on");
6403         this.hideAction();
6404         this.fireEvent("deactivate", this.tabPanel, this);
6405     },
6406
6407     hideAction : function(){
6408         this.bodyEl.hide();
6409         this.bodyEl.setStyle("position", "absolute");
6410         this.bodyEl.setLeft("-20000px");
6411         this.bodyEl.setTop("-20000px");
6412     },
6413
6414     showAction : function(){
6415         this.bodyEl.setStyle("position", "relative");
6416         this.bodyEl.setTop("");
6417         this.bodyEl.setLeft("");
6418         this.bodyEl.show();
6419     },
6420
6421     /**
6422      * Set the tooltip for the tab.
6423      * @param {String} tooltip The tab's tooltip
6424      */
6425     setTooltip : function(text){
6426         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6427             this.textEl.dom.qtip = text;
6428             this.textEl.dom.removeAttribute('title');
6429         }else{
6430             this.textEl.dom.title = text;
6431         }
6432     },
6433
6434     onTabClick : function(e){
6435         e.preventDefault();
6436         this.tabPanel.activate(this.id);
6437     },
6438
6439     onTabMouseDown : function(e){
6440         e.preventDefault();
6441         this.tabPanel.activate(this.id);
6442     },
6443
6444     getWidth : function(){
6445         return this.inner.getWidth();
6446     },
6447
6448     setWidth : function(width){
6449         var iwidth = width - this.pnode.getPadding("lr");
6450         this.inner.setWidth(iwidth);
6451         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6452         this.pnode.setWidth(width);
6453     },
6454
6455     /**
6456      * Show or hide the tab
6457      * @param {Boolean} hidden True to hide or false to show.
6458      */
6459     setHidden : function(hidden){
6460         this.hidden = hidden;
6461         this.pnode.setStyle("display", hidden ? "none" : "");
6462     },
6463
6464     /**
6465      * Returns true if this tab is "hidden"
6466      * @return {Boolean}
6467      */
6468     isHidden : function(){
6469         return this.hidden;
6470     },
6471
6472     /**
6473      * Returns the text for this tab
6474      * @return {String}
6475      */
6476     getText : function(){
6477         return this.text;
6478     },
6479
6480     autoSize : function(){
6481         //this.el.beginMeasure();
6482         this.textEl.setWidth(1);
6483         /*
6484          *  #2804 [new] Tabs in Roojs
6485          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6486          */
6487         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6488         //this.el.endMeasure();
6489     },
6490
6491     /**
6492      * Sets the text for the tab (Note: this also sets the tooltip text)
6493      * @param {String} text The tab's text and tooltip
6494      */
6495     setText : function(text){
6496         this.text = text;
6497         this.textEl.update(text);
6498         this.setTooltip(text);
6499         if(!this.tabPanel.resizeTabs){
6500             this.autoSize();
6501         }
6502     },
6503     /**
6504      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6505      */
6506     activate : function(){
6507         this.tabPanel.activate(this.id);
6508     },
6509
6510     /**
6511      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6512      */
6513     disable : function(){
6514         if(this.tabPanel.active != this){
6515             this.disabled = true;
6516             this.pnode.addClass("disabled");
6517         }
6518     },
6519
6520     /**
6521      * Enables this TabPanelItem if it was previously disabled.
6522      */
6523     enable : function(){
6524         this.disabled = false;
6525         this.pnode.removeClass("disabled");
6526     },
6527
6528     /**
6529      * Sets the content for this TabPanelItem.
6530      * @param {String} content The content
6531      * @param {Boolean} loadScripts true to look for and load scripts
6532      */
6533     setContent : function(content, loadScripts){
6534         this.bodyEl.update(content, loadScripts);
6535     },
6536
6537     /**
6538      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6539      * @return {Roo.UpdateManager} The UpdateManager
6540      */
6541     getUpdateManager : function(){
6542         return this.bodyEl.getUpdateManager();
6543     },
6544
6545     /**
6546      * Set a URL to be used to load the content for this TabPanelItem.
6547      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6548      * @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)
6549      * @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)
6550      * @return {Roo.UpdateManager} The UpdateManager
6551      */
6552     setUrl : function(url, params, loadOnce){
6553         if(this.refreshDelegate){
6554             this.un('activate', this.refreshDelegate);
6555         }
6556         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6557         this.on("activate", this.refreshDelegate);
6558         return this.bodyEl.getUpdateManager();
6559     },
6560
6561     /** @private */
6562     _handleRefresh : function(url, params, loadOnce){
6563         if(!loadOnce || !this.loaded){
6564             var updater = this.bodyEl.getUpdateManager();
6565             updater.update(url, params, this._setLoaded.createDelegate(this));
6566         }
6567     },
6568
6569     /**
6570      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6571      *   Will fail silently if the setUrl method has not been called.
6572      *   This does not activate the panel, just updates its content.
6573      */
6574     refresh : function(){
6575         if(this.refreshDelegate){
6576            this.loaded = false;
6577            this.refreshDelegate();
6578         }
6579     },
6580
6581     /** @private */
6582     _setLoaded : function(){
6583         this.loaded = true;
6584     },
6585
6586     /** @private */
6587     closeClick : function(e){
6588         var o = {};
6589         e.stopEvent();
6590         this.fireEvent("beforeclose", this, o);
6591         if(o.cancel !== true){
6592             this.tabPanel.removeTab(this.id);
6593         }
6594     },
6595     /**
6596      * The text displayed in the tooltip for the close icon.
6597      * @type String
6598      */
6599     closeText : "Close this tab"
6600 });
6601
6602 /** @private */
6603 Roo.TabPanel.prototype.createStrip = function(container){
6604     var strip = document.createElement("div");
6605     strip.className = "x-tabs-wrap";
6606     container.appendChild(strip);
6607     return strip;
6608 };
6609 /** @private */
6610 Roo.TabPanel.prototype.createStripList = function(strip){
6611     // div wrapper for retard IE
6612     // returns the "tr" element.
6613     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6614         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6615         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6616     return strip.firstChild.firstChild.firstChild.firstChild;
6617 };
6618 /** @private */
6619 Roo.TabPanel.prototype.createBody = function(container){
6620     var body = document.createElement("div");
6621     Roo.id(body, "tab-body");
6622     Roo.fly(body).addClass("x-tabs-body");
6623     container.appendChild(body);
6624     return body;
6625 };
6626 /** @private */
6627 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6628     var body = Roo.getDom(id);
6629     if(!body){
6630         body = document.createElement("div");
6631         body.id = id;
6632     }
6633     Roo.fly(body).addClass("x-tabs-item-body");
6634     bodyEl.insertBefore(body, bodyEl.firstChild);
6635     return body;
6636 };
6637 /** @private */
6638 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6639     var td = document.createElement("td");
6640     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6641     //stripEl.appendChild(td);
6642     if(closable){
6643         td.className = "x-tabs-closable";
6644         if(!this.closeTpl){
6645             this.closeTpl = new Roo.Template(
6646                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6647                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6648                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6649             );
6650         }
6651         var el = this.closeTpl.overwrite(td, {"text": text});
6652         var close = el.getElementsByTagName("div")[0];
6653         var inner = el.getElementsByTagName("em")[0];
6654         return {"el": el, "close": close, "inner": inner};
6655     } else {
6656         if(!this.tabTpl){
6657             this.tabTpl = new Roo.Template(
6658                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6659                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6660             );
6661         }
6662         var el = this.tabTpl.overwrite(td, {"text": text});
6663         var inner = el.getElementsByTagName("em")[0];
6664         return {"el": el, "inner": inner};
6665     }
6666 };/*
6667  * Based on:
6668  * Ext JS Library 1.1.1
6669  * Copyright(c) 2006-2007, Ext JS, LLC.
6670  *
6671  * Originally Released Under LGPL - original licence link has changed is not relivant.
6672  *
6673  * Fork - LGPL
6674  * <script type="text/javascript">
6675  */
6676
6677 /**
6678  * @class Roo.Button
6679  * @extends Roo.util.Observable
6680  * Simple Button class
6681  * @cfg {String} text The button text
6682  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6683  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6684  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6685  * @cfg {Object} scope The scope of the handler
6686  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6687  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6688  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6689  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6690  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6691  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6692    applies if enableToggle = true)
6693  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6694  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6695   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6696  * @constructor
6697  * Create a new button
6698  * @param {Object} config The config object
6699  */
6700 Roo.Button = function(renderTo, config)
6701 {
6702     if (!config) {
6703         config = renderTo;
6704         renderTo = config.renderTo || false;
6705     }
6706     
6707     Roo.apply(this, config);
6708     this.addEvents({
6709         /**
6710              * @event click
6711              * Fires when this button is clicked
6712              * @param {Button} this
6713              * @param {EventObject} e The click event
6714              */
6715             "click" : true,
6716         /**
6717              * @event toggle
6718              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6719              * @param {Button} this
6720              * @param {Boolean} pressed
6721              */
6722             "toggle" : true,
6723         /**
6724              * @event mouseover
6725              * Fires when the mouse hovers over the button
6726              * @param {Button} this
6727              * @param {Event} e The event object
6728              */
6729         'mouseover' : true,
6730         /**
6731              * @event mouseout
6732              * Fires when the mouse exits the button
6733              * @param {Button} this
6734              * @param {Event} e The event object
6735              */
6736         'mouseout': true,
6737          /**
6738              * @event render
6739              * Fires when the button is rendered
6740              * @param {Button} this
6741              */
6742         'render': true
6743     });
6744     if(this.menu){
6745         this.menu = Roo.menu.MenuMgr.get(this.menu);
6746     }
6747     // register listeners first!!  - so render can be captured..
6748     Roo.util.Observable.call(this);
6749     if(renderTo){
6750         this.render(renderTo);
6751     }
6752     
6753   
6754 };
6755
6756 Roo.extend(Roo.Button, Roo.util.Observable, {
6757     /**
6758      * 
6759      */
6760     
6761     /**
6762      * Read-only. True if this button is hidden
6763      * @type Boolean
6764      */
6765     hidden : false,
6766     /**
6767      * Read-only. True if this button is disabled
6768      * @type Boolean
6769      */
6770     disabled : false,
6771     /**
6772      * Read-only. True if this button is pressed (only if enableToggle = true)
6773      * @type Boolean
6774      */
6775     pressed : false,
6776
6777     /**
6778      * @cfg {Number} tabIndex 
6779      * The DOM tabIndex for this button (defaults to undefined)
6780      */
6781     tabIndex : undefined,
6782
6783     /**
6784      * @cfg {Boolean} enableToggle
6785      * True to enable pressed/not pressed toggling (defaults to false)
6786      */
6787     enableToggle: false,
6788     /**
6789      * @cfg {Mixed} menu
6790      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6791      */
6792     menu : undefined,
6793     /**
6794      * @cfg {String} menuAlign
6795      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6796      */
6797     menuAlign : "tl-bl?",
6798
6799     /**
6800      * @cfg {String} iconCls
6801      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6802      */
6803     iconCls : undefined,
6804     /**
6805      * @cfg {String} type
6806      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6807      */
6808     type : 'button',
6809
6810     // private
6811     menuClassTarget: 'tr',
6812
6813     /**
6814      * @cfg {String} clickEvent
6815      * The type of event to map to the button's event handler (defaults to 'click')
6816      */
6817     clickEvent : 'click',
6818
6819     /**
6820      * @cfg {Boolean} handleMouseEvents
6821      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6822      */
6823     handleMouseEvents : true,
6824
6825     /**
6826      * @cfg {String} tooltipType
6827      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6828      */
6829     tooltipType : 'qtip',
6830
6831     /**
6832      * @cfg {String} cls
6833      * A CSS class to apply to the button's main element.
6834      */
6835     
6836     /**
6837      * @cfg {Roo.Template} template (Optional)
6838      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6839      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6840      * require code modifications if required elements (e.g. a button) aren't present.
6841      */
6842
6843     // private
6844     render : function(renderTo){
6845         var btn;
6846         if(this.hideParent){
6847             this.parentEl = Roo.get(renderTo);
6848         }
6849         if(!this.dhconfig){
6850             if(!this.template){
6851                 if(!Roo.Button.buttonTemplate){
6852                     // hideous table template
6853                     Roo.Button.buttonTemplate = new Roo.Template(
6854                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6855                         '<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>',
6856                         "</tr></tbody></table>");
6857                 }
6858                 this.template = Roo.Button.buttonTemplate;
6859             }
6860             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6861             var btnEl = btn.child("button:first");
6862             btnEl.on('focus', this.onFocus, this);
6863             btnEl.on('blur', this.onBlur, this);
6864             if(this.cls){
6865                 btn.addClass(this.cls);
6866             }
6867             if(this.icon){
6868                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6869             }
6870             if(this.iconCls){
6871                 btnEl.addClass(this.iconCls);
6872                 if(!this.cls){
6873                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6874                 }
6875             }
6876             if(this.tabIndex !== undefined){
6877                 btnEl.dom.tabIndex = this.tabIndex;
6878             }
6879             if(this.tooltip){
6880                 if(typeof this.tooltip == 'object'){
6881                     Roo.QuickTips.tips(Roo.apply({
6882                           target: btnEl.id
6883                     }, this.tooltip));
6884                 } else {
6885                     btnEl.dom[this.tooltipType] = this.tooltip;
6886                 }
6887             }
6888         }else{
6889             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6890         }
6891         this.el = btn;
6892         if(this.id){
6893             this.el.dom.id = this.el.id = this.id;
6894         }
6895         if(this.menu){
6896             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6897             this.menu.on("show", this.onMenuShow, this);
6898             this.menu.on("hide", this.onMenuHide, this);
6899         }
6900         btn.addClass("x-btn");
6901         if(Roo.isIE && !Roo.isIE7){
6902             this.autoWidth.defer(1, this);
6903         }else{
6904             this.autoWidth();
6905         }
6906         if(this.handleMouseEvents){
6907             btn.on("mouseover", this.onMouseOver, this);
6908             btn.on("mouseout", this.onMouseOut, this);
6909             btn.on("mousedown", this.onMouseDown, this);
6910         }
6911         btn.on(this.clickEvent, this.onClick, this);
6912         //btn.on("mouseup", this.onMouseUp, this);
6913         if(this.hidden){
6914             this.hide();
6915         }
6916         if(this.disabled){
6917             this.disable();
6918         }
6919         Roo.ButtonToggleMgr.register(this);
6920         if(this.pressed){
6921             this.el.addClass("x-btn-pressed");
6922         }
6923         if(this.repeat){
6924             var repeater = new Roo.util.ClickRepeater(btn,
6925                 typeof this.repeat == "object" ? this.repeat : {}
6926             );
6927             repeater.on("click", this.onClick,  this);
6928         }
6929         
6930         this.fireEvent('render', this);
6931         
6932     },
6933     /**
6934      * Returns the button's underlying element
6935      * @return {Roo.Element} The element
6936      */
6937     getEl : function(){
6938         return this.el;  
6939     },
6940     
6941     /**
6942      * Destroys this Button and removes any listeners.
6943      */
6944     destroy : function(){
6945         Roo.ButtonToggleMgr.unregister(this);
6946         this.el.removeAllListeners();
6947         this.purgeListeners();
6948         this.el.remove();
6949     },
6950
6951     // private
6952     autoWidth : function(){
6953         if(this.el){
6954             this.el.setWidth("auto");
6955             if(Roo.isIE7 && Roo.isStrict){
6956                 var ib = this.el.child('button');
6957                 if(ib && ib.getWidth() > 20){
6958                     ib.clip();
6959                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6960                 }
6961             }
6962             if(this.minWidth){
6963                 if(this.hidden){
6964                     this.el.beginMeasure();
6965                 }
6966                 if(this.el.getWidth() < this.minWidth){
6967                     this.el.setWidth(this.minWidth);
6968                 }
6969                 if(this.hidden){
6970                     this.el.endMeasure();
6971                 }
6972             }
6973         }
6974     },
6975
6976     /**
6977      * Assigns this button's click handler
6978      * @param {Function} handler The function to call when the button is clicked
6979      * @param {Object} scope (optional) Scope for the function passed in
6980      */
6981     setHandler : function(handler, scope){
6982         this.handler = handler;
6983         this.scope = scope;  
6984     },
6985     
6986     /**
6987      * Sets this button's text
6988      * @param {String} text The button text
6989      */
6990     setText : function(text){
6991         this.text = text;
6992         if(this.el){
6993             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6994         }
6995         this.autoWidth();
6996     },
6997     
6998     /**
6999      * Gets the text for this button
7000      * @return {String} The button text
7001      */
7002     getText : function(){
7003         return this.text;  
7004     },
7005     
7006     /**
7007      * Show this button
7008      */
7009     show: function(){
7010         this.hidden = false;
7011         if(this.el){
7012             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
7013         }
7014     },
7015     
7016     /**
7017      * Hide this button
7018      */
7019     hide: function(){
7020         this.hidden = true;
7021         if(this.el){
7022             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
7023         }
7024     },
7025     
7026     /**
7027      * Convenience function for boolean show/hide
7028      * @param {Boolean} visible True to show, false to hide
7029      */
7030     setVisible: function(visible){
7031         if(visible) {
7032             this.show();
7033         }else{
7034             this.hide();
7035         }
7036     },
7037     
7038     /**
7039      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7040      * @param {Boolean} state (optional) Force a particular state
7041      */
7042     toggle : function(state){
7043         state = state === undefined ? !this.pressed : state;
7044         if(state != this.pressed){
7045             if(state){
7046                 this.el.addClass("x-btn-pressed");
7047                 this.pressed = true;
7048                 this.fireEvent("toggle", this, true);
7049             }else{
7050                 this.el.removeClass("x-btn-pressed");
7051                 this.pressed = false;
7052                 this.fireEvent("toggle", this, false);
7053             }
7054             if(this.toggleHandler){
7055                 this.toggleHandler.call(this.scope || this, this, state);
7056             }
7057         }
7058     },
7059     
7060     /**
7061      * Focus the button
7062      */
7063     focus : function(){
7064         this.el.child('button:first').focus();
7065     },
7066     
7067     /**
7068      * Disable this button
7069      */
7070     disable : function(){
7071         if(this.el){
7072             this.el.addClass("x-btn-disabled");
7073         }
7074         this.disabled = true;
7075     },
7076     
7077     /**
7078      * Enable this button
7079      */
7080     enable : function(){
7081         if(this.el){
7082             this.el.removeClass("x-btn-disabled");
7083         }
7084         this.disabled = false;
7085     },
7086
7087     /**
7088      * Convenience function for boolean enable/disable
7089      * @param {Boolean} enabled True to enable, false to disable
7090      */
7091     setDisabled : function(v){
7092         this[v !== true ? "enable" : "disable"]();
7093     },
7094
7095     // private
7096     onClick : function(e)
7097     {
7098         if(e){
7099             e.preventDefault();
7100         }
7101         if(e.button != 0){
7102             return;
7103         }
7104         if(!this.disabled){
7105             if(this.enableToggle){
7106                 this.toggle();
7107             }
7108             if(this.menu && !this.menu.isVisible()){
7109                 this.menu.show(this.el, this.menuAlign);
7110             }
7111             this.fireEvent("click", this, e);
7112             if(this.handler){
7113                 this.el.removeClass("x-btn-over");
7114                 this.handler.call(this.scope || this, this, e);
7115             }
7116         }
7117     },
7118     // private
7119     onMouseOver : function(e){
7120         if(!this.disabled){
7121             this.el.addClass("x-btn-over");
7122             this.fireEvent('mouseover', this, e);
7123         }
7124     },
7125     // private
7126     onMouseOut : function(e){
7127         if(!e.within(this.el,  true)){
7128             this.el.removeClass("x-btn-over");
7129             this.fireEvent('mouseout', this, e);
7130         }
7131     },
7132     // private
7133     onFocus : function(e){
7134         if(!this.disabled){
7135             this.el.addClass("x-btn-focus");
7136         }
7137     },
7138     // private
7139     onBlur : function(e){
7140         this.el.removeClass("x-btn-focus");
7141     },
7142     // private
7143     onMouseDown : function(e){
7144         if(!this.disabled && e.button == 0){
7145             this.el.addClass("x-btn-click");
7146             Roo.get(document).on('mouseup', this.onMouseUp, this);
7147         }
7148     },
7149     // private
7150     onMouseUp : function(e){
7151         if(e.button == 0){
7152             this.el.removeClass("x-btn-click");
7153             Roo.get(document).un('mouseup', this.onMouseUp, this);
7154         }
7155     },
7156     // private
7157     onMenuShow : function(e){
7158         this.el.addClass("x-btn-menu-active");
7159     },
7160     // private
7161     onMenuHide : function(e){
7162         this.el.removeClass("x-btn-menu-active");
7163     }   
7164 });
7165
7166 // Private utility class used by Button
7167 Roo.ButtonToggleMgr = function(){
7168    var groups = {};
7169    
7170    function toggleGroup(btn, state){
7171        if(state){
7172            var g = groups[btn.toggleGroup];
7173            for(var i = 0, l = g.length; i < l; i++){
7174                if(g[i] != btn){
7175                    g[i].toggle(false);
7176                }
7177            }
7178        }
7179    }
7180    
7181    return {
7182        register : function(btn){
7183            if(!btn.toggleGroup){
7184                return;
7185            }
7186            var g = groups[btn.toggleGroup];
7187            if(!g){
7188                g = groups[btn.toggleGroup] = [];
7189            }
7190            g.push(btn);
7191            btn.on("toggle", toggleGroup);
7192        },
7193        
7194        unregister : function(btn){
7195            if(!btn.toggleGroup){
7196                return;
7197            }
7198            var g = groups[btn.toggleGroup];
7199            if(g){
7200                g.remove(btn);
7201                btn.un("toggle", toggleGroup);
7202            }
7203        }
7204    };
7205 }();/*
7206  * Based on:
7207  * Ext JS Library 1.1.1
7208  * Copyright(c) 2006-2007, Ext JS, LLC.
7209  *
7210  * Originally Released Under LGPL - original licence link has changed is not relivant.
7211  *
7212  * Fork - LGPL
7213  * <script type="text/javascript">
7214  */
7215  
7216 /**
7217  * @class Roo.SplitButton
7218  * @extends Roo.Button
7219  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7220  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
7221  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7222  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7223  * @cfg {String} arrowTooltip The title attribute of the arrow
7224  * @constructor
7225  * Create a new menu button
7226  * @param {String/HTMLElement/Element} renderTo The element to append the button to
7227  * @param {Object} config The config object
7228  */
7229 Roo.SplitButton = function(renderTo, config){
7230     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7231     /**
7232      * @event arrowclick
7233      * Fires when this button's arrow is clicked
7234      * @param {SplitButton} this
7235      * @param {EventObject} e The click event
7236      */
7237     this.addEvents({"arrowclick":true});
7238 };
7239
7240 Roo.extend(Roo.SplitButton, Roo.Button, {
7241     render : function(renderTo){
7242         // this is one sweet looking template!
7243         var tpl = new Roo.Template(
7244             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7245             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7246             '<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>',
7247             "</tbody></table></td><td>",
7248             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7249             '<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>',
7250             "</tbody></table></td></tr></table>"
7251         );
7252         var btn = tpl.append(renderTo, [this.text, this.type], true);
7253         var btnEl = btn.child("button");
7254         if(this.cls){
7255             btn.addClass(this.cls);
7256         }
7257         if(this.icon){
7258             btnEl.setStyle('background-image', 'url(' +this.icon +')');
7259         }
7260         if(this.iconCls){
7261             btnEl.addClass(this.iconCls);
7262             if(!this.cls){
7263                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7264             }
7265         }
7266         this.el = btn;
7267         if(this.handleMouseEvents){
7268             btn.on("mouseover", this.onMouseOver, this);
7269             btn.on("mouseout", this.onMouseOut, this);
7270             btn.on("mousedown", this.onMouseDown, this);
7271             btn.on("mouseup", this.onMouseUp, this);
7272         }
7273         btn.on(this.clickEvent, this.onClick, this);
7274         if(this.tooltip){
7275             if(typeof this.tooltip == 'object'){
7276                 Roo.QuickTips.tips(Roo.apply({
7277                       target: btnEl.id
7278                 }, this.tooltip));
7279             } else {
7280                 btnEl.dom[this.tooltipType] = this.tooltip;
7281             }
7282         }
7283         if(this.arrowTooltip){
7284             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7285         }
7286         if(this.hidden){
7287             this.hide();
7288         }
7289         if(this.disabled){
7290             this.disable();
7291         }
7292         if(this.pressed){
7293             this.el.addClass("x-btn-pressed");
7294         }
7295         if(Roo.isIE && !Roo.isIE7){
7296             this.autoWidth.defer(1, this);
7297         }else{
7298             this.autoWidth();
7299         }
7300         if(this.menu){
7301             this.menu.on("show", this.onMenuShow, this);
7302             this.menu.on("hide", this.onMenuHide, this);
7303         }
7304         this.fireEvent('render', this);
7305     },
7306
7307     // private
7308     autoWidth : function(){
7309         if(this.el){
7310             var tbl = this.el.child("table:first");
7311             var tbl2 = this.el.child("table:last");
7312             this.el.setWidth("auto");
7313             tbl.setWidth("auto");
7314             if(Roo.isIE7 && Roo.isStrict){
7315                 var ib = this.el.child('button:first');
7316                 if(ib && ib.getWidth() > 20){
7317                     ib.clip();
7318                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7319                 }
7320             }
7321             if(this.minWidth){
7322                 if(this.hidden){
7323                     this.el.beginMeasure();
7324                 }
7325                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7326                     tbl.setWidth(this.minWidth-tbl2.getWidth());
7327                 }
7328                 if(this.hidden){
7329                     this.el.endMeasure();
7330                 }
7331             }
7332             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7333         } 
7334     },
7335     /**
7336      * Sets this button's click handler
7337      * @param {Function} handler The function to call when the button is clicked
7338      * @param {Object} scope (optional) Scope for the function passed above
7339      */
7340     setHandler : function(handler, scope){
7341         this.handler = handler;
7342         this.scope = scope;  
7343     },
7344     
7345     /**
7346      * Sets this button's arrow click handler
7347      * @param {Function} handler The function to call when the arrow is clicked
7348      * @param {Object} scope (optional) Scope for the function passed above
7349      */
7350     setArrowHandler : function(handler, scope){
7351         this.arrowHandler = handler;
7352         this.scope = scope;  
7353     },
7354     
7355     /**
7356      * Focus the button
7357      */
7358     focus : function(){
7359         if(this.el){
7360             this.el.child("button:first").focus();
7361         }
7362     },
7363
7364     // private
7365     onClick : function(e){
7366         e.preventDefault();
7367         if(!this.disabled){
7368             if(e.getTarget(".x-btn-menu-arrow-wrap")){
7369                 if(this.menu && !this.menu.isVisible()){
7370                     this.menu.show(this.el, this.menuAlign);
7371                 }
7372                 this.fireEvent("arrowclick", this, e);
7373                 if(this.arrowHandler){
7374                     this.arrowHandler.call(this.scope || this, this, e);
7375                 }
7376             }else{
7377                 this.fireEvent("click", this, e);
7378                 if(this.handler){
7379                     this.handler.call(this.scope || this, this, e);
7380                 }
7381             }
7382         }
7383     },
7384     // private
7385     onMouseDown : function(e){
7386         if(!this.disabled){
7387             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7388         }
7389     },
7390     // private
7391     onMouseUp : function(e){
7392         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7393     }   
7394 });
7395
7396
7397 // backwards compat
7398 Roo.MenuButton = Roo.SplitButton;/*
7399  * Based on:
7400  * Ext JS Library 1.1.1
7401  * Copyright(c) 2006-2007, Ext JS, LLC.
7402  *
7403  * Originally Released Under LGPL - original licence link has changed is not relivant.
7404  *
7405  * Fork - LGPL
7406  * <script type="text/javascript">
7407  */
7408
7409 /**
7410  * @class Roo.Toolbar
7411  * Basic Toolbar class.
7412  * @constructor
7413  * Creates a new Toolbar
7414  * @param {Object} container The config object
7415  */ 
7416 Roo.Toolbar = function(container, buttons, config)
7417 {
7418     /// old consturctor format still supported..
7419     if(container instanceof Array){ // omit the container for later rendering
7420         buttons = container;
7421         config = buttons;
7422         container = null;
7423     }
7424     if (typeof(container) == 'object' && container.xtype) {
7425         config = container;
7426         container = config.container;
7427         buttons = config.buttons || []; // not really - use items!!
7428     }
7429     var xitems = [];
7430     if (config && config.items) {
7431         xitems = config.items;
7432         delete config.items;
7433     }
7434     Roo.apply(this, config);
7435     this.buttons = buttons;
7436     
7437     if(container){
7438         this.render(container);
7439     }
7440     this.xitems = xitems;
7441     Roo.each(xitems, function(b) {
7442         this.add(b);
7443     }, this);
7444     
7445 };
7446
7447 Roo.Toolbar.prototype = {
7448     /**
7449      * @cfg {Array} items
7450      * array of button configs or elements to add (will be converted to a MixedCollection)
7451      */
7452     
7453     /**
7454      * @cfg {String/HTMLElement/Element} container
7455      * The id or element that will contain the toolbar
7456      */
7457     // private
7458     render : function(ct){
7459         this.el = Roo.get(ct);
7460         if(this.cls){
7461             this.el.addClass(this.cls);
7462         }
7463         // using a table allows for vertical alignment
7464         // 100% width is needed by Safari...
7465         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7466         this.tr = this.el.child("tr", true);
7467         var autoId = 0;
7468         this.items = new Roo.util.MixedCollection(false, function(o){
7469             return o.id || ("item" + (++autoId));
7470         });
7471         if(this.buttons){
7472             this.add.apply(this, this.buttons);
7473             delete this.buttons;
7474         }
7475     },
7476
7477     /**
7478      * Adds element(s) to the toolbar -- this function takes a variable number of 
7479      * arguments of mixed type and adds them to the toolbar.
7480      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7481      * <ul>
7482      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7483      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7484      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7485      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7486      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7487      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7488      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7489      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7490      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7491      * </ul>
7492      * @param {Mixed} arg2
7493      * @param {Mixed} etc.
7494      */
7495     add : function(){
7496         var a = arguments, l = a.length;
7497         for(var i = 0; i < l; i++){
7498             this._add(a[i]);
7499         }
7500     },
7501     // private..
7502     _add : function(el) {
7503         
7504         if (el.xtype) {
7505             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7506         }
7507         
7508         if (el.applyTo){ // some kind of form field
7509             return this.addField(el);
7510         } 
7511         if (el.render){ // some kind of Toolbar.Item
7512             return this.addItem(el);
7513         }
7514         if (typeof el == "string"){ // string
7515             if(el == "separator" || el == "-"){
7516                 return this.addSeparator();
7517             }
7518             if (el == " "){
7519                 return this.addSpacer();
7520             }
7521             if(el == "->"){
7522                 return this.addFill();
7523             }
7524             return this.addText(el);
7525             
7526         }
7527         if(el.tagName){ // element
7528             return this.addElement(el);
7529         }
7530         if(typeof el == "object"){ // must be button config?
7531             return this.addButton(el);
7532         }
7533         // and now what?!?!
7534         return false;
7535         
7536     },
7537     
7538     /**
7539      * Add an Xtype element
7540      * @param {Object} xtype Xtype Object
7541      * @return {Object} created Object
7542      */
7543     addxtype : function(e){
7544         return this.add(e);  
7545     },
7546     
7547     /**
7548      * Returns the Element for this toolbar.
7549      * @return {Roo.Element}
7550      */
7551     getEl : function(){
7552         return this.el;  
7553     },
7554     
7555     /**
7556      * Adds a separator
7557      * @return {Roo.Toolbar.Item} The separator item
7558      */
7559     addSeparator : function(){
7560         return this.addItem(new Roo.Toolbar.Separator());
7561     },
7562
7563     /**
7564      * Adds a spacer element
7565      * @return {Roo.Toolbar.Spacer} The spacer item
7566      */
7567     addSpacer : function(){
7568         return this.addItem(new Roo.Toolbar.Spacer());
7569     },
7570
7571     /**
7572      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7573      * @return {Roo.Toolbar.Fill} The fill item
7574      */
7575     addFill : function(){
7576         return this.addItem(new Roo.Toolbar.Fill());
7577     },
7578
7579     /**
7580      * Adds any standard HTML element to the toolbar
7581      * @param {String/HTMLElement/Element} el The element or id of the element to add
7582      * @return {Roo.Toolbar.Item} The element's item
7583      */
7584     addElement : function(el){
7585         return this.addItem(new Roo.Toolbar.Item(el));
7586     },
7587     /**
7588      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7589      * @type Roo.util.MixedCollection  
7590      */
7591     items : false,
7592      
7593     /**
7594      * Adds any Toolbar.Item or subclass
7595      * @param {Roo.Toolbar.Item} item
7596      * @return {Roo.Toolbar.Item} The item
7597      */
7598     addItem : function(item){
7599         var td = this.nextBlock();
7600         item.render(td);
7601         this.items.add(item);
7602         return item;
7603     },
7604     
7605     /**
7606      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7607      * @param {Object/Array} config A button config or array of configs
7608      * @return {Roo.Toolbar.Button/Array}
7609      */
7610     addButton : function(config){
7611         if(config instanceof Array){
7612             var buttons = [];
7613             for(var i = 0, len = config.length; i < len; i++) {
7614                 buttons.push(this.addButton(config[i]));
7615             }
7616             return buttons;
7617         }
7618         var b = config;
7619         if(!(config instanceof Roo.Toolbar.Button)){
7620             b = config.split ?
7621                 new Roo.Toolbar.SplitButton(config) :
7622                 new Roo.Toolbar.Button(config);
7623         }
7624         var td = this.nextBlock();
7625         b.render(td);
7626         this.items.add(b);
7627         return b;
7628     },
7629     
7630     /**
7631      * Adds text to the toolbar
7632      * @param {String} text The text to add
7633      * @return {Roo.Toolbar.Item} The element's item
7634      */
7635     addText : function(text){
7636         return this.addItem(new Roo.Toolbar.TextItem(text));
7637     },
7638     
7639     /**
7640      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7641      * @param {Number} index The index where the item is to be inserted
7642      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7643      * @return {Roo.Toolbar.Button/Item}
7644      */
7645     insertButton : function(index, item){
7646         if(item instanceof Array){
7647             var buttons = [];
7648             for(var i = 0, len = item.length; i < len; i++) {
7649                buttons.push(this.insertButton(index + i, item[i]));
7650             }
7651             return buttons;
7652         }
7653         if (!(item instanceof Roo.Toolbar.Button)){
7654            item = new Roo.Toolbar.Button(item);
7655         }
7656         var td = document.createElement("td");
7657         this.tr.insertBefore(td, this.tr.childNodes[index]);
7658         item.render(td);
7659         this.items.insert(index, item);
7660         return item;
7661     },
7662     
7663     /**
7664      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7665      * @param {Object} config
7666      * @return {Roo.Toolbar.Item} The element's item
7667      */
7668     addDom : function(config, returnEl){
7669         var td = this.nextBlock();
7670         Roo.DomHelper.overwrite(td, config);
7671         var ti = new Roo.Toolbar.Item(td.firstChild);
7672         ti.render(td);
7673         this.items.add(ti);
7674         return ti;
7675     },
7676
7677     /**
7678      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7679      * @type Roo.util.MixedCollection  
7680      */
7681     fields : false,
7682     
7683     /**
7684      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7685      * Note: the field should not have been rendered yet. For a field that has already been
7686      * rendered, use {@link #addElement}.
7687      * @param {Roo.form.Field} field
7688      * @return {Roo.ToolbarItem}
7689      */
7690      
7691       
7692     addField : function(field) {
7693         if (!this.fields) {
7694             var autoId = 0;
7695             this.fields = new Roo.util.MixedCollection(false, function(o){
7696                 return o.id || ("item" + (++autoId));
7697             });
7698
7699         }
7700         
7701         var td = this.nextBlock();
7702         field.render(td);
7703         var ti = new Roo.Toolbar.Item(td.firstChild);
7704         ti.render(td);
7705         this.items.add(ti);
7706         this.fields.add(field);
7707         return ti;
7708     },
7709     /**
7710      * Hide the toolbar
7711      * @method hide
7712      */
7713      
7714       
7715     hide : function()
7716     {
7717         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7718         this.el.child('div').hide();
7719     },
7720     /**
7721      * Show the toolbar
7722      * @method show
7723      */
7724     show : function()
7725     {
7726         this.el.child('div').show();
7727     },
7728       
7729     // private
7730     nextBlock : function(){
7731         var td = document.createElement("td");
7732         this.tr.appendChild(td);
7733         return td;
7734     },
7735
7736     // private
7737     destroy : function(){
7738         if(this.items){ // rendered?
7739             Roo.destroy.apply(Roo, this.items.items);
7740         }
7741         if(this.fields){ // rendered?
7742             Roo.destroy.apply(Roo, this.fields.items);
7743         }
7744         Roo.Element.uncache(this.el, this.tr);
7745     }
7746 };
7747
7748 /**
7749  * @class Roo.Toolbar.Item
7750  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7751  * @constructor
7752  * Creates a new Item
7753  * @param {HTMLElement} el 
7754  */
7755 Roo.Toolbar.Item = function(el){
7756     var cfg = {};
7757     if (typeof (el.xtype) != 'undefined') {
7758         cfg = el;
7759         el = cfg.el;
7760     }
7761     
7762     this.el = Roo.getDom(el);
7763     this.id = Roo.id(this.el);
7764     this.hidden = false;
7765     
7766     this.addEvents({
7767          /**
7768              * @event render
7769              * Fires when the button is rendered
7770              * @param {Button} this
7771              */
7772         'render': true
7773     });
7774     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7775 };
7776 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7777 //Roo.Toolbar.Item.prototype = {
7778     
7779     /**
7780      * Get this item's HTML Element
7781      * @return {HTMLElement}
7782      */
7783     getEl : function(){
7784        return this.el;  
7785     },
7786
7787     // private
7788     render : function(td){
7789         
7790          this.td = td;
7791         td.appendChild(this.el);
7792         
7793         this.fireEvent('render', this);
7794     },
7795     
7796     /**
7797      * Removes and destroys this item.
7798      */
7799     destroy : function(){
7800         this.td.parentNode.removeChild(this.td);
7801     },
7802     
7803     /**
7804      * Shows this item.
7805      */
7806     show: function(){
7807         this.hidden = false;
7808         this.td.style.display = "";
7809     },
7810     
7811     /**
7812      * Hides this item.
7813      */
7814     hide: function(){
7815         this.hidden = true;
7816         this.td.style.display = "none";
7817     },
7818     
7819     /**
7820      * Convenience function for boolean show/hide.
7821      * @param {Boolean} visible true to show/false to hide
7822      */
7823     setVisible: function(visible){
7824         if(visible) {
7825             this.show();
7826         }else{
7827             this.hide();
7828         }
7829     },
7830     
7831     /**
7832      * Try to focus this item.
7833      */
7834     focus : function(){
7835         Roo.fly(this.el).focus();
7836     },
7837     
7838     /**
7839      * Disables this item.
7840      */
7841     disable : function(){
7842         Roo.fly(this.td).addClass("x-item-disabled");
7843         this.disabled = true;
7844         this.el.disabled = true;
7845     },
7846     
7847     /**
7848      * Enables this item.
7849      */
7850     enable : function(){
7851         Roo.fly(this.td).removeClass("x-item-disabled");
7852         this.disabled = false;
7853         this.el.disabled = false;
7854     }
7855 });
7856
7857
7858 /**
7859  * @class Roo.Toolbar.Separator
7860  * @extends Roo.Toolbar.Item
7861  * A simple toolbar separator class
7862  * @constructor
7863  * Creates a new Separator
7864  */
7865 Roo.Toolbar.Separator = function(cfg){
7866     
7867     var s = document.createElement("span");
7868     s.className = "ytb-sep";
7869     if (cfg) {
7870         cfg.el = s;
7871     }
7872     
7873     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7874 };
7875 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7876     enable:Roo.emptyFn,
7877     disable:Roo.emptyFn,
7878     focus:Roo.emptyFn
7879 });
7880
7881 /**
7882  * @class Roo.Toolbar.Spacer
7883  * @extends Roo.Toolbar.Item
7884  * A simple element that adds extra horizontal space to a toolbar.
7885  * @constructor
7886  * Creates a new Spacer
7887  */
7888 Roo.Toolbar.Spacer = function(cfg){
7889     var s = document.createElement("div");
7890     s.className = "ytb-spacer";
7891     if (cfg) {
7892         cfg.el = s;
7893     }
7894     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7895 };
7896 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7897     enable:Roo.emptyFn,
7898     disable:Roo.emptyFn,
7899     focus:Roo.emptyFn
7900 });
7901
7902 /**
7903  * @class Roo.Toolbar.Fill
7904  * @extends Roo.Toolbar.Spacer
7905  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7906  * @constructor
7907  * Creates a new Spacer
7908  */
7909 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7910     // private
7911     render : function(td){
7912         td.style.width = '100%';
7913         Roo.Toolbar.Fill.superclass.render.call(this, td);
7914     }
7915 });
7916
7917 /**
7918  * @class Roo.Toolbar.TextItem
7919  * @extends Roo.Toolbar.Item
7920  * A simple class that renders text directly into a toolbar.
7921  * @constructor
7922  * Creates a new TextItem
7923  * @param {String} text
7924  */
7925 Roo.Toolbar.TextItem = function(cfg){
7926     var  text = cfg || "";
7927     if (typeof(cfg) == 'object') {
7928         text = cfg.text || "";
7929     }  else {
7930         cfg = null;
7931     }
7932     var s = document.createElement("span");
7933     s.className = "ytb-text";
7934     s.innerHTML = text;
7935     if (cfg) {
7936         cfg.el  = s;
7937     }
7938     
7939     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7940 };
7941 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7942     
7943      
7944     enable:Roo.emptyFn,
7945     disable:Roo.emptyFn,
7946     focus:Roo.emptyFn
7947 });
7948
7949 /**
7950  * @class Roo.Toolbar.Button
7951  * @extends Roo.Button
7952  * A button that renders into a toolbar.
7953  * @constructor
7954  * Creates a new Button
7955  * @param {Object} config A standard {@link Roo.Button} config object
7956  */
7957 Roo.Toolbar.Button = function(config){
7958     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7959 };
7960 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7961     render : function(td){
7962         this.td = td;
7963         Roo.Toolbar.Button.superclass.render.call(this, td);
7964     },
7965     
7966     /**
7967      * Removes and destroys this button
7968      */
7969     destroy : function(){
7970         Roo.Toolbar.Button.superclass.destroy.call(this);
7971         this.td.parentNode.removeChild(this.td);
7972     },
7973     
7974     /**
7975      * Shows this button
7976      */
7977     show: function(){
7978         this.hidden = false;
7979         this.td.style.display = "";
7980     },
7981     
7982     /**
7983      * Hides this button
7984      */
7985     hide: function(){
7986         this.hidden = true;
7987         this.td.style.display = "none";
7988     },
7989
7990     /**
7991      * Disables this item
7992      */
7993     disable : function(){
7994         Roo.fly(this.td).addClass("x-item-disabled");
7995         this.disabled = true;
7996     },
7997
7998     /**
7999      * Enables this item
8000      */
8001     enable : function(){
8002         Roo.fly(this.td).removeClass("x-item-disabled");
8003         this.disabled = false;
8004     }
8005 });
8006 // backwards compat
8007 Roo.ToolbarButton = Roo.Toolbar.Button;
8008
8009 /**
8010  * @class Roo.Toolbar.SplitButton
8011  * @extends Roo.SplitButton
8012  * A menu button that renders into a toolbar.
8013  * @constructor
8014  * Creates a new SplitButton
8015  * @param {Object} config A standard {@link Roo.SplitButton} config object
8016  */
8017 Roo.Toolbar.SplitButton = function(config){
8018     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
8019 };
8020 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
8021     render : function(td){
8022         this.td = td;
8023         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8024     },
8025     
8026     /**
8027      * Removes and destroys this button
8028      */
8029     destroy : function(){
8030         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8031         this.td.parentNode.removeChild(this.td);
8032     },
8033     
8034     /**
8035      * Shows this button
8036      */
8037     show: function(){
8038         this.hidden = false;
8039         this.td.style.display = "";
8040     },
8041     
8042     /**
8043      * Hides this button
8044      */
8045     hide: function(){
8046         this.hidden = true;
8047         this.td.style.display = "none";
8048     }
8049 });
8050
8051 // backwards compat
8052 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8053  * Based on:
8054  * Ext JS Library 1.1.1
8055  * Copyright(c) 2006-2007, Ext JS, LLC.
8056  *
8057  * Originally Released Under LGPL - original licence link has changed is not relivant.
8058  *
8059  * Fork - LGPL
8060  * <script type="text/javascript">
8061  */
8062  
8063 /**
8064  * @class Roo.PagingToolbar
8065  * @extends Roo.Toolbar
8066  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8067  * @constructor
8068  * Create a new PagingToolbar
8069  * @param {Object} config The config object
8070  */
8071 Roo.PagingToolbar = function(el, ds, config)
8072 {
8073     // old args format still supported... - xtype is prefered..
8074     if (typeof(el) == 'object' && el.xtype) {
8075         // created from xtype...
8076         config = el;
8077         ds = el.dataSource;
8078         el = config.container;
8079     }
8080     var items = [];
8081     if (config.items) {
8082         items = config.items;
8083         config.items = [];
8084     }
8085     
8086     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8087     this.ds = ds;
8088     this.cursor = 0;
8089     this.renderButtons(this.el);
8090     this.bind(ds);
8091     
8092     // supprot items array.
8093    
8094     Roo.each(items, function(e) {
8095         this.add(Roo.factory(e));
8096     },this);
8097     
8098 };
8099
8100 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8101     /**
8102      * @cfg {Roo.data.Store} dataSource
8103      * The underlying data store providing the paged data
8104      */
8105     /**
8106      * @cfg {String/HTMLElement/Element} container
8107      * container The id or element that will contain the toolbar
8108      */
8109     /**
8110      * @cfg {Boolean} displayInfo
8111      * True to display the displayMsg (defaults to false)
8112      */
8113     /**
8114      * @cfg {Number} pageSize
8115      * The number of records to display per page (defaults to 20)
8116      */
8117     pageSize: 20,
8118     /**
8119      * @cfg {String} displayMsg
8120      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8121      */
8122     displayMsg : 'Displaying {0} - {1} of {2}',
8123     /**
8124      * @cfg {String} emptyMsg
8125      * The message to display when no records are found (defaults to "No data to display")
8126      */
8127     emptyMsg : 'No data to display',
8128     /**
8129      * Customizable piece of the default paging text (defaults to "Page")
8130      * @type String
8131      */
8132     beforePageText : "Page",
8133     /**
8134      * Customizable piece of the default paging text (defaults to "of %0")
8135      * @type String
8136      */
8137     afterPageText : "of {0}",
8138     /**
8139      * Customizable piece of the default paging text (defaults to "First Page")
8140      * @type String
8141      */
8142     firstText : "First Page",
8143     /**
8144      * Customizable piece of the default paging text (defaults to "Previous Page")
8145      * @type String
8146      */
8147     prevText : "Previous Page",
8148     /**
8149      * Customizable piece of the default paging text (defaults to "Next Page")
8150      * @type String
8151      */
8152     nextText : "Next Page",
8153     /**
8154      * Customizable piece of the default paging text (defaults to "Last Page")
8155      * @type String
8156      */
8157     lastText : "Last Page",
8158     /**
8159      * Customizable piece of the default paging text (defaults to "Refresh")
8160      * @type String
8161      */
8162     refreshText : "Refresh",
8163
8164     // private
8165     renderButtons : function(el){
8166         Roo.PagingToolbar.superclass.render.call(this, el);
8167         this.first = this.addButton({
8168             tooltip: this.firstText,
8169             cls: "x-btn-icon x-grid-page-first",
8170             disabled: true,
8171             handler: this.onClick.createDelegate(this, ["first"])
8172         });
8173         this.prev = this.addButton({
8174             tooltip: this.prevText,
8175             cls: "x-btn-icon x-grid-page-prev",
8176             disabled: true,
8177             handler: this.onClick.createDelegate(this, ["prev"])
8178         });
8179         //this.addSeparator();
8180         this.add(this.beforePageText);
8181         this.field = Roo.get(this.addDom({
8182            tag: "input",
8183            type: "text",
8184            size: "3",
8185            value: "1",
8186            cls: "x-grid-page-number"
8187         }).el);
8188         this.field.on("keydown", this.onPagingKeydown, this);
8189         this.field.on("focus", function(){this.dom.select();});
8190         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8191         this.field.setHeight(18);
8192         //this.addSeparator();
8193         this.next = this.addButton({
8194             tooltip: this.nextText,
8195             cls: "x-btn-icon x-grid-page-next",
8196             disabled: true,
8197             handler: this.onClick.createDelegate(this, ["next"])
8198         });
8199         this.last = this.addButton({
8200             tooltip: this.lastText,
8201             cls: "x-btn-icon x-grid-page-last",
8202             disabled: true,
8203             handler: this.onClick.createDelegate(this, ["last"])
8204         });
8205         //this.addSeparator();
8206         this.loading = this.addButton({
8207             tooltip: this.refreshText,
8208             cls: "x-btn-icon x-grid-loading",
8209             handler: this.onClick.createDelegate(this, ["refresh"])
8210         });
8211
8212         if(this.displayInfo){
8213             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8214         }
8215     },
8216
8217     // private
8218     updateInfo : function(){
8219         if(this.displayEl){
8220             var count = this.ds.getCount();
8221             var msg = count == 0 ?
8222                 this.emptyMsg :
8223                 String.format(
8224                     this.displayMsg,
8225                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
8226                 );
8227             this.displayEl.update(msg);
8228         }
8229     },
8230
8231     // private
8232     onLoad : function(ds, r, o){
8233        this.cursor = o.params ? o.params.start : 0;
8234        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8235
8236        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8237        this.field.dom.value = ap;
8238        this.first.setDisabled(ap == 1);
8239        this.prev.setDisabled(ap == 1);
8240        this.next.setDisabled(ap == ps);
8241        this.last.setDisabled(ap == ps);
8242        this.loading.enable();
8243        this.updateInfo();
8244     },
8245
8246     // private
8247     getPageData : function(){
8248         var total = this.ds.getTotalCount();
8249         return {
8250             total : total,
8251             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8252             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8253         };
8254     },
8255
8256     // private
8257     onLoadError : function(){
8258         this.loading.enable();
8259     },
8260
8261     // private
8262     onPagingKeydown : function(e){
8263         var k = e.getKey();
8264         var d = this.getPageData();
8265         if(k == e.RETURN){
8266             var v = this.field.dom.value, pageNum;
8267             if(!v || isNaN(pageNum = parseInt(v, 10))){
8268                 this.field.dom.value = d.activePage;
8269                 return;
8270             }
8271             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8272             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8273             e.stopEvent();
8274         }
8275         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))
8276         {
8277           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8278           this.field.dom.value = pageNum;
8279           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8280           e.stopEvent();
8281         }
8282         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8283         {
8284           var v = this.field.dom.value, pageNum; 
8285           var increment = (e.shiftKey) ? 10 : 1;
8286           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8287             increment *= -1;
8288           }
8289           if(!v || isNaN(pageNum = parseInt(v, 10))) {
8290             this.field.dom.value = d.activePage;
8291             return;
8292           }
8293           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8294           {
8295             this.field.dom.value = parseInt(v, 10) + increment;
8296             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8297             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8298           }
8299           e.stopEvent();
8300         }
8301     },
8302
8303     // private
8304     beforeLoad : function(){
8305         if(this.loading){
8306             this.loading.disable();
8307         }
8308     },
8309
8310     // private
8311     onClick : function(which){
8312         var ds = this.ds;
8313         switch(which){
8314             case "first":
8315                 ds.load({params:{start: 0, limit: this.pageSize}});
8316             break;
8317             case "prev":
8318                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8319             break;
8320             case "next":
8321                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8322             break;
8323             case "last":
8324                 var total = ds.getTotalCount();
8325                 var extra = total % this.pageSize;
8326                 var lastStart = extra ? (total - extra) : total-this.pageSize;
8327                 ds.load({params:{start: lastStart, limit: this.pageSize}});
8328             break;
8329             case "refresh":
8330                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8331             break;
8332         }
8333     },
8334
8335     /**
8336      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8337      * @param {Roo.data.Store} store The data store to unbind
8338      */
8339     unbind : function(ds){
8340         ds.un("beforeload", this.beforeLoad, this);
8341         ds.un("load", this.onLoad, this);
8342         ds.un("loadexception", this.onLoadError, this);
8343         ds.un("remove", this.updateInfo, this);
8344         ds.un("add", this.updateInfo, this);
8345         this.ds = undefined;
8346     },
8347
8348     /**
8349      * Binds the paging toolbar to the specified {@link Roo.data.Store}
8350      * @param {Roo.data.Store} store The data store to bind
8351      */
8352     bind : function(ds){
8353         ds.on("beforeload", this.beforeLoad, this);
8354         ds.on("load", this.onLoad, this);
8355         ds.on("loadexception", this.onLoadError, this);
8356         ds.on("remove", this.updateInfo, this);
8357         ds.on("add", this.updateInfo, this);
8358         this.ds = ds;
8359     }
8360 });/*
8361  * Based on:
8362  * Ext JS Library 1.1.1
8363  * Copyright(c) 2006-2007, Ext JS, LLC.
8364  *
8365  * Originally Released Under LGPL - original licence link has changed is not relivant.
8366  *
8367  * Fork - LGPL
8368  * <script type="text/javascript">
8369  */
8370
8371 /**
8372  * @class Roo.Resizable
8373  * @extends Roo.util.Observable
8374  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8375  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8376  * 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
8377  * the element will be wrapped for you automatically.</p>
8378  * <p>Here is the list of valid resize handles:</p>
8379  * <pre>
8380 Value   Description
8381 ------  -------------------
8382  'n'     north
8383  's'     south
8384  'e'     east
8385  'w'     west
8386  'nw'    northwest
8387  'sw'    southwest
8388  'se'    southeast
8389  'ne'    northeast
8390  'hd'    horizontal drag
8391  'all'   all
8392 </pre>
8393  * <p>Here's an example showing the creation of a typical Resizable:</p>
8394  * <pre><code>
8395 var resizer = new Roo.Resizable("element-id", {
8396     handles: 'all',
8397     minWidth: 200,
8398     minHeight: 100,
8399     maxWidth: 500,
8400     maxHeight: 400,
8401     pinned: true
8402 });
8403 resizer.on("resize", myHandler);
8404 </code></pre>
8405  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8406  * resizer.east.setDisplayed(false);</p>
8407  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8408  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8409  * resize operation's new size (defaults to [0, 0])
8410  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8411  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8412  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8413  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8414  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8415  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8416  * @cfg {Number} width The width of the element in pixels (defaults to null)
8417  * @cfg {Number} height The height of the element in pixels (defaults to null)
8418  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8419  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8420  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8421  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8422  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8423  * in favor of the handles config option (defaults to false)
8424  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8425  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8426  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8427  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8428  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8429  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8430  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8431  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8432  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8433  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8434  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8435  * @constructor
8436  * Create a new resizable component
8437  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8438  * @param {Object} config configuration options
8439   */
8440 Roo.Resizable = function(el, config)
8441 {
8442     this.el = Roo.get(el);
8443
8444     if(config && config.wrap){
8445         config.resizeChild = this.el;
8446         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8447         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8448         this.el.setStyle("overflow", "hidden");
8449         this.el.setPositioning(config.resizeChild.getPositioning());
8450         config.resizeChild.clearPositioning();
8451         if(!config.width || !config.height){
8452             var csize = config.resizeChild.getSize();
8453             this.el.setSize(csize.width, csize.height);
8454         }
8455         if(config.pinned && !config.adjustments){
8456             config.adjustments = "auto";
8457         }
8458     }
8459
8460     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8461     this.proxy.unselectable();
8462     this.proxy.enableDisplayMode('block');
8463
8464     Roo.apply(this, config);
8465
8466     if(this.pinned){
8467         this.disableTrackOver = true;
8468         this.el.addClass("x-resizable-pinned");
8469     }
8470     // if the element isn't positioned, make it relative
8471     var position = this.el.getStyle("position");
8472     if(position != "absolute" && position != "fixed"){
8473         this.el.setStyle("position", "relative");
8474     }
8475     if(!this.handles){ // no handles passed, must be legacy style
8476         this.handles = 's,e,se';
8477         if(this.multiDirectional){
8478             this.handles += ',n,w';
8479         }
8480     }
8481     if(this.handles == "all"){
8482         this.handles = "n s e w ne nw se sw";
8483     }
8484     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8485     var ps = Roo.Resizable.positions;
8486     for(var i = 0, len = hs.length; i < len; i++){
8487         if(hs[i] && ps[hs[i]]){
8488             var pos = ps[hs[i]];
8489             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8490         }
8491     }
8492     // legacy
8493     this.corner = this.southeast;
8494     
8495     // updateBox = the box can move..
8496     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8497         this.updateBox = true;
8498     }
8499
8500     this.activeHandle = null;
8501
8502     if(this.resizeChild){
8503         if(typeof this.resizeChild == "boolean"){
8504             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8505         }else{
8506             this.resizeChild = Roo.get(this.resizeChild, true);
8507         }
8508     }
8509     
8510     if(this.adjustments == "auto"){
8511         var rc = this.resizeChild;
8512         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8513         if(rc && (hw || hn)){
8514             rc.position("relative");
8515             rc.setLeft(hw ? hw.el.getWidth() : 0);
8516             rc.setTop(hn ? hn.el.getHeight() : 0);
8517         }
8518         this.adjustments = [
8519             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8520             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8521         ];
8522     }
8523
8524     if(this.draggable){
8525         this.dd = this.dynamic ?
8526             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8527         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8528     }
8529
8530     // public events
8531     this.addEvents({
8532         /**
8533          * @event beforeresize
8534          * Fired before resize is allowed. Set enabled to false to cancel resize.
8535          * @param {Roo.Resizable} this
8536          * @param {Roo.EventObject} e The mousedown event
8537          */
8538         "beforeresize" : true,
8539         /**
8540          * @event resizing
8541          * Fired a resizing.
8542          * @param {Roo.Resizable} this
8543          * @param {Number} x The new x position
8544          * @param {Number} y The new y position
8545          * @param {Number} w The new w width
8546          * @param {Number} h The new h hight
8547          * @param {Roo.EventObject} e The mouseup event
8548          */
8549         "resizing" : true,
8550         /**
8551          * @event resize
8552          * Fired after a resize.
8553          * @param {Roo.Resizable} this
8554          * @param {Number} width The new width
8555          * @param {Number} height The new height
8556          * @param {Roo.EventObject} e The mouseup event
8557          */
8558         "resize" : true
8559     });
8560
8561     if(this.width !== null && this.height !== null){
8562         this.resizeTo(this.width, this.height);
8563     }else{
8564         this.updateChildSize();
8565     }
8566     if(Roo.isIE){
8567         this.el.dom.style.zoom = 1;
8568     }
8569     Roo.Resizable.superclass.constructor.call(this);
8570 };
8571
8572 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8573         resizeChild : false,
8574         adjustments : [0, 0],
8575         minWidth : 5,
8576         minHeight : 5,
8577         maxWidth : 10000,
8578         maxHeight : 10000,
8579         enabled : true,
8580         animate : false,
8581         duration : .35,
8582         dynamic : false,
8583         handles : false,
8584         multiDirectional : false,
8585         disableTrackOver : false,
8586         easing : 'easeOutStrong',
8587         widthIncrement : 0,
8588         heightIncrement : 0,
8589         pinned : false,
8590         width : null,
8591         height : null,
8592         preserveRatio : false,
8593         transparent: false,
8594         minX: 0,
8595         minY: 0,
8596         draggable: false,
8597
8598         /**
8599          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8600          */
8601         constrainTo: undefined,
8602         /**
8603          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8604          */
8605         resizeRegion: undefined,
8606
8607
8608     /**
8609      * Perform a manual resize
8610      * @param {Number} width
8611      * @param {Number} height
8612      */
8613     resizeTo : function(width, height){
8614         this.el.setSize(width, height);
8615         this.updateChildSize();
8616         this.fireEvent("resize", this, width, height, null);
8617     },
8618
8619     // private
8620     startSizing : function(e, handle){
8621         this.fireEvent("beforeresize", this, e);
8622         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8623
8624             if(!this.overlay){
8625                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8626                 this.overlay.unselectable();
8627                 this.overlay.enableDisplayMode("block");
8628                 this.overlay.on("mousemove", this.onMouseMove, this);
8629                 this.overlay.on("mouseup", this.onMouseUp, this);
8630             }
8631             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8632
8633             this.resizing = true;
8634             this.startBox = this.el.getBox();
8635             this.startPoint = e.getXY();
8636             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8637                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8638
8639             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8640             this.overlay.show();
8641
8642             if(this.constrainTo) {
8643                 var ct = Roo.get(this.constrainTo);
8644                 this.resizeRegion = ct.getRegion().adjust(
8645                     ct.getFrameWidth('t'),
8646                     ct.getFrameWidth('l'),
8647                     -ct.getFrameWidth('b'),
8648                     -ct.getFrameWidth('r')
8649                 );
8650             }
8651
8652             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8653             this.proxy.show();
8654             this.proxy.setBox(this.startBox);
8655             if(!this.dynamic){
8656                 this.proxy.setStyle('visibility', 'visible');
8657             }
8658         }
8659     },
8660
8661     // private
8662     onMouseDown : function(handle, e){
8663         if(this.enabled){
8664             e.stopEvent();
8665             this.activeHandle = handle;
8666             this.startSizing(e, handle);
8667         }
8668     },
8669
8670     // private
8671     onMouseUp : function(e){
8672         var size = this.resizeElement();
8673         this.resizing = false;
8674         this.handleOut();
8675         this.overlay.hide();
8676         this.proxy.hide();
8677         this.fireEvent("resize", this, size.width, size.height, e);
8678     },
8679
8680     // private
8681     updateChildSize : function(){
8682         
8683         if(this.resizeChild){
8684             var el = this.el;
8685             var child = this.resizeChild;
8686             var adj = this.adjustments;
8687             if(el.dom.offsetWidth){
8688                 var b = el.getSize(true);
8689                 child.setSize(b.width+adj[0], b.height+adj[1]);
8690             }
8691             // Second call here for IE
8692             // The first call enables instant resizing and
8693             // the second call corrects scroll bars if they
8694             // exist
8695             if(Roo.isIE){
8696                 setTimeout(function(){
8697                     if(el.dom.offsetWidth){
8698                         var b = el.getSize(true);
8699                         child.setSize(b.width+adj[0], b.height+adj[1]);
8700                     }
8701                 }, 10);
8702             }
8703         }
8704     },
8705
8706     // private
8707     snap : function(value, inc, min){
8708         if(!inc || !value) {
8709             return value;
8710         }
8711         var newValue = value;
8712         var m = value % inc;
8713         if(m > 0){
8714             if(m > (inc/2)){
8715                 newValue = value + (inc-m);
8716             }else{
8717                 newValue = value - m;
8718             }
8719         }
8720         return Math.max(min, newValue);
8721     },
8722
8723     // private
8724     resizeElement : function(){
8725         var box = this.proxy.getBox();
8726         if(this.updateBox){
8727             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8728         }else{
8729             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8730         }
8731         this.updateChildSize();
8732         if(!this.dynamic){
8733             this.proxy.hide();
8734         }
8735         return box;
8736     },
8737
8738     // private
8739     constrain : function(v, diff, m, mx){
8740         if(v - diff < m){
8741             diff = v - m;
8742         }else if(v - diff > mx){
8743             diff = mx - v;
8744         }
8745         return diff;
8746     },
8747
8748     // private
8749     onMouseMove : function(e){
8750         
8751         if(this.enabled){
8752             try{// try catch so if something goes wrong the user doesn't get hung
8753
8754             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8755                 return;
8756             }
8757
8758             //var curXY = this.startPoint;
8759             var curSize = this.curSize || this.startBox;
8760             var x = this.startBox.x, y = this.startBox.y;
8761             var ox = x, oy = y;
8762             var w = curSize.width, h = curSize.height;
8763             var ow = w, oh = h;
8764             var mw = this.minWidth, mh = this.minHeight;
8765             var mxw = this.maxWidth, mxh = this.maxHeight;
8766             var wi = this.widthIncrement;
8767             var hi = this.heightIncrement;
8768
8769             var eventXY = e.getXY();
8770             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8771             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8772
8773             var pos = this.activeHandle.position;
8774
8775             switch(pos){
8776                 case "east":
8777                     w += diffX;
8778                     w = Math.min(Math.max(mw, w), mxw);
8779                     break;
8780              
8781                 case "south":
8782                     h += diffY;
8783                     h = Math.min(Math.max(mh, h), mxh);
8784                     break;
8785                 case "southeast":
8786                     w += diffX;
8787                     h += diffY;
8788                     w = Math.min(Math.max(mw, w), mxw);
8789                     h = Math.min(Math.max(mh, h), mxh);
8790                     break;
8791                 case "north":
8792                     diffY = this.constrain(h, diffY, mh, mxh);
8793                     y += diffY;
8794                     h -= diffY;
8795                     break;
8796                 case "hdrag":
8797                     
8798                     if (wi) {
8799                         var adiffX = Math.abs(diffX);
8800                         var sub = (adiffX % wi); // how much 
8801                         if (sub > (wi/2)) { // far enough to snap
8802                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8803                         } else {
8804                             // remove difference.. 
8805                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8806                         }
8807                     }
8808                     x += diffX;
8809                     x = Math.max(this.minX, x);
8810                     break;
8811                 case "west":
8812                     diffX = this.constrain(w, diffX, mw, mxw);
8813                     x += diffX;
8814                     w -= diffX;
8815                     break;
8816                 case "northeast":
8817                     w += diffX;
8818                     w = Math.min(Math.max(mw, w), mxw);
8819                     diffY = this.constrain(h, diffY, mh, mxh);
8820                     y += diffY;
8821                     h -= diffY;
8822                     break;
8823                 case "northwest":
8824                     diffX = this.constrain(w, diffX, mw, mxw);
8825                     diffY = this.constrain(h, diffY, mh, mxh);
8826                     y += diffY;
8827                     h -= diffY;
8828                     x += diffX;
8829                     w -= diffX;
8830                     break;
8831                case "southwest":
8832                     diffX = this.constrain(w, diffX, mw, mxw);
8833                     h += diffY;
8834                     h = Math.min(Math.max(mh, h), mxh);
8835                     x += diffX;
8836                     w -= diffX;
8837                     break;
8838             }
8839
8840             var sw = this.snap(w, wi, mw);
8841             var sh = this.snap(h, hi, mh);
8842             if(sw != w || sh != h){
8843                 switch(pos){
8844                     case "northeast":
8845                         y -= sh - h;
8846                     break;
8847                     case "north":
8848                         y -= sh - h;
8849                         break;
8850                     case "southwest":
8851                         x -= sw - w;
8852                     break;
8853                     case "west":
8854                         x -= sw - w;
8855                         break;
8856                     case "northwest":
8857                         x -= sw - w;
8858                         y -= sh - h;
8859                     break;
8860                 }
8861                 w = sw;
8862                 h = sh;
8863             }
8864
8865             if(this.preserveRatio){
8866                 switch(pos){
8867                     case "southeast":
8868                     case "east":
8869                         h = oh * (w/ow);
8870                         h = Math.min(Math.max(mh, h), mxh);
8871                         w = ow * (h/oh);
8872                        break;
8873                     case "south":
8874                         w = ow * (h/oh);
8875                         w = Math.min(Math.max(mw, w), mxw);
8876                         h = oh * (w/ow);
8877                         break;
8878                     case "northeast":
8879                         w = ow * (h/oh);
8880                         w = Math.min(Math.max(mw, w), mxw);
8881                         h = oh * (w/ow);
8882                     break;
8883                     case "north":
8884                         var tw = w;
8885                         w = ow * (h/oh);
8886                         w = Math.min(Math.max(mw, w), mxw);
8887                         h = oh * (w/ow);
8888                         x += (tw - w) / 2;
8889                         break;
8890                     case "southwest":
8891                         h = oh * (w/ow);
8892                         h = Math.min(Math.max(mh, h), mxh);
8893                         var tw = w;
8894                         w = ow * (h/oh);
8895                         x += tw - w;
8896                         break;
8897                     case "west":
8898                         var th = h;
8899                         h = oh * (w/ow);
8900                         h = Math.min(Math.max(mh, h), mxh);
8901                         y += (th - h) / 2;
8902                         var tw = w;
8903                         w = ow * (h/oh);
8904                         x += tw - w;
8905                        break;
8906                     case "northwest":
8907                         var tw = w;
8908                         var th = h;
8909                         h = oh * (w/ow);
8910                         h = Math.min(Math.max(mh, h), mxh);
8911                         w = ow * (h/oh);
8912                         y += th - h;
8913                         x += tw - w;
8914                        break;
8915
8916                 }
8917             }
8918             if (pos == 'hdrag') {
8919                 w = ow;
8920             }
8921             this.proxy.setBounds(x, y, w, h);
8922             if(this.dynamic){
8923                 this.resizeElement();
8924             }
8925             }catch(e){}
8926         }
8927         this.fireEvent("resizing", this, x, y, w, h, e);
8928     },
8929
8930     // private
8931     handleOver : function(){
8932         if(this.enabled){
8933             this.el.addClass("x-resizable-over");
8934         }
8935     },
8936
8937     // private
8938     handleOut : function(){
8939         if(!this.resizing){
8940             this.el.removeClass("x-resizable-over");
8941         }
8942     },
8943
8944     /**
8945      * Returns the element this component is bound to.
8946      * @return {Roo.Element}
8947      */
8948     getEl : function(){
8949         return this.el;
8950     },
8951
8952     /**
8953      * Returns the resizeChild element (or null).
8954      * @return {Roo.Element}
8955      */
8956     getResizeChild : function(){
8957         return this.resizeChild;
8958     },
8959     groupHandler : function()
8960     {
8961         
8962     },
8963     /**
8964      * Destroys this resizable. If the element was wrapped and
8965      * removeEl is not true then the element remains.
8966      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8967      */
8968     destroy : function(removeEl){
8969         this.proxy.remove();
8970         if(this.overlay){
8971             this.overlay.removeAllListeners();
8972             this.overlay.remove();
8973         }
8974         var ps = Roo.Resizable.positions;
8975         for(var k in ps){
8976             if(typeof ps[k] != "function" && this[ps[k]]){
8977                 var h = this[ps[k]];
8978                 h.el.removeAllListeners();
8979                 h.el.remove();
8980             }
8981         }
8982         if(removeEl){
8983             this.el.update("");
8984             this.el.remove();
8985         }
8986     }
8987 });
8988
8989 // private
8990 // hash to map config positions to true positions
8991 Roo.Resizable.positions = {
8992     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8993     hd: "hdrag"
8994 };
8995
8996 // private
8997 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8998     if(!this.tpl){
8999         // only initialize the template if resizable is used
9000         var tpl = Roo.DomHelper.createTemplate(
9001             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
9002         );
9003         tpl.compile();
9004         Roo.Resizable.Handle.prototype.tpl = tpl;
9005     }
9006     this.position = pos;
9007     this.rz = rz;
9008     // show north drag fro topdra
9009     var handlepos = pos == 'hdrag' ? 'north' : pos;
9010     
9011     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
9012     if (pos == 'hdrag') {
9013         this.el.setStyle('cursor', 'pointer');
9014     }
9015     this.el.unselectable();
9016     if(transparent){
9017         this.el.setOpacity(0);
9018     }
9019     this.el.on("mousedown", this.onMouseDown, this);
9020     if(!disableTrackOver){
9021         this.el.on("mouseover", this.onMouseOver, this);
9022         this.el.on("mouseout", this.onMouseOut, this);
9023     }
9024 };
9025
9026 // private
9027 Roo.Resizable.Handle.prototype = {
9028     afterResize : function(rz){
9029         Roo.log('after?');
9030         // do nothing
9031     },
9032     // private
9033     onMouseDown : function(e){
9034         this.rz.onMouseDown(this, e);
9035     },
9036     // private
9037     onMouseOver : function(e){
9038         this.rz.handleOver(this, e);
9039     },
9040     // private
9041     onMouseOut : function(e){
9042         this.rz.handleOut(this, e);
9043     }
9044 };/*
9045  * Based on:
9046  * Ext JS Library 1.1.1
9047  * Copyright(c) 2006-2007, Ext JS, LLC.
9048  *
9049  * Originally Released Under LGPL - original licence link has changed is not relivant.
9050  *
9051  * Fork - LGPL
9052  * <script type="text/javascript">
9053  */
9054
9055 /**
9056  * @class Roo.Editor
9057  * @extends Roo.Component
9058  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9059  * @constructor
9060  * Create a new Editor
9061  * @param {Roo.form.Field} field The Field object (or descendant)
9062  * @param {Object} config The config object
9063  */
9064 Roo.Editor = function(field, config){
9065     Roo.Editor.superclass.constructor.call(this, config);
9066     this.field = field;
9067     this.addEvents({
9068         /**
9069              * @event beforestartedit
9070              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
9071              * false from the handler of this event.
9072              * @param {Editor} this
9073              * @param {Roo.Element} boundEl The underlying element bound to this editor
9074              * @param {Mixed} value The field value being set
9075              */
9076         "beforestartedit" : true,
9077         /**
9078              * @event startedit
9079              * Fires when this editor is displayed
9080              * @param {Roo.Element} boundEl The underlying element bound to this editor
9081              * @param {Mixed} value The starting field value
9082              */
9083         "startedit" : true,
9084         /**
9085              * @event beforecomplete
9086              * Fires after a change has been made to the field, but before the change is reflected in the underlying
9087              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
9088              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9089              * event will not fire since no edit actually occurred.
9090              * @param {Editor} this
9091              * @param {Mixed} value The current field value
9092              * @param {Mixed} startValue The original field value
9093              */
9094         "beforecomplete" : true,
9095         /**
9096              * @event complete
9097              * Fires after editing is complete and any changed value has been written to the underlying field.
9098              * @param {Editor} this
9099              * @param {Mixed} value The current field value
9100              * @param {Mixed} startValue The original field value
9101              */
9102         "complete" : true,
9103         /**
9104          * @event specialkey
9105          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9106          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9107          * @param {Roo.form.Field} this
9108          * @param {Roo.EventObject} e The event object
9109          */
9110         "specialkey" : true
9111     });
9112 };
9113
9114 Roo.extend(Roo.Editor, Roo.Component, {
9115     /**
9116      * @cfg {Boolean/String} autosize
9117      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9118      * or "height" to adopt the height only (defaults to false)
9119      */
9120     /**
9121      * @cfg {Boolean} revertInvalid
9122      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9123      * validation fails (defaults to true)
9124      */
9125     /**
9126      * @cfg {Boolean} ignoreNoChange
9127      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9128      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
9129      * will never be ignored.
9130      */
9131     /**
9132      * @cfg {Boolean} hideEl
9133      * False to keep the bound element visible while the editor is displayed (defaults to true)
9134      */
9135     /**
9136      * @cfg {Mixed} value
9137      * The data value of the underlying field (defaults to "")
9138      */
9139     value : "",
9140     /**
9141      * @cfg {String} alignment
9142      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9143      */
9144     alignment: "c-c?",
9145     /**
9146      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9147      * for bottom-right shadow (defaults to "frame")
9148      */
9149     shadow : "frame",
9150     /**
9151      * @cfg {Boolean} constrain True to constrain the editor to the viewport
9152      */
9153     constrain : false,
9154     /**
9155      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9156      */
9157     completeOnEnter : false,
9158     /**
9159      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9160      */
9161     cancelOnEsc : false,
9162     /**
9163      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9164      */
9165     updateEl : false,
9166
9167     // private
9168     onRender : function(ct, position){
9169         this.el = new Roo.Layer({
9170             shadow: this.shadow,
9171             cls: "x-editor",
9172             parentEl : ct,
9173             shim : this.shim,
9174             shadowOffset:4,
9175             id: this.id,
9176             constrain: this.constrain
9177         });
9178         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9179         if(this.field.msgTarget != 'title'){
9180             this.field.msgTarget = 'qtip';
9181         }
9182         this.field.render(this.el);
9183         if(Roo.isGecko){
9184             this.field.el.dom.setAttribute('autocomplete', 'off');
9185         }
9186         this.field.on("specialkey", this.onSpecialKey, this);
9187         if(this.swallowKeys){
9188             this.field.el.swallowEvent(['keydown','keypress']);
9189         }
9190         this.field.show();
9191         this.field.on("blur", this.onBlur, this);
9192         if(this.field.grow){
9193             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
9194         }
9195     },
9196
9197     onSpecialKey : function(field, e)
9198     {
9199         //Roo.log('editor onSpecialKey');
9200         if(this.completeOnEnter && e.getKey() == e.ENTER){
9201             e.stopEvent();
9202             this.completeEdit();
9203             return;
9204         }
9205         // do not fire special key otherwise it might hide close the editor...
9206         if(e.getKey() == e.ENTER){    
9207             return;
9208         }
9209         if(this.cancelOnEsc && e.getKey() == e.ESC){
9210             this.cancelEdit();
9211             return;
9212         } 
9213         this.fireEvent('specialkey', field, e);
9214     
9215     },
9216
9217     /**
9218      * Starts the editing process and shows the editor.
9219      * @param {String/HTMLElement/Element} el The element to edit
9220      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9221       * to the innerHTML of el.
9222      */
9223     startEdit : function(el, value){
9224         if(this.editing){
9225             this.completeEdit();
9226         }
9227         this.boundEl = Roo.get(el);
9228         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9229         if(!this.rendered){
9230             this.render(this.parentEl || document.body);
9231         }
9232         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9233             return;
9234         }
9235         this.startValue = v;
9236         this.field.setValue(v);
9237         if(this.autoSize){
9238             var sz = this.boundEl.getSize();
9239             switch(this.autoSize){
9240                 case "width":
9241                 this.setSize(sz.width,  "");
9242                 break;
9243                 case "height":
9244                 this.setSize("",  sz.height);
9245                 break;
9246                 default:
9247                 this.setSize(sz.width,  sz.height);
9248             }
9249         }
9250         this.el.alignTo(this.boundEl, this.alignment);
9251         this.editing = true;
9252         if(Roo.QuickTips){
9253             Roo.QuickTips.disable();
9254         }
9255         this.show();
9256     },
9257
9258     /**
9259      * Sets the height and width of this editor.
9260      * @param {Number} width The new width
9261      * @param {Number} height The new height
9262      */
9263     setSize : function(w, h){
9264         this.field.setSize(w, h);
9265         if(this.el){
9266             this.el.sync();
9267         }
9268     },
9269
9270     /**
9271      * Realigns the editor to the bound field based on the current alignment config value.
9272      */
9273     realign : function(){
9274         this.el.alignTo(this.boundEl, this.alignment);
9275     },
9276
9277     /**
9278      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9279      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9280      */
9281     completeEdit : function(remainVisible){
9282         if(!this.editing){
9283             return;
9284         }
9285         var v = this.getValue();
9286         if(this.revertInvalid !== false && !this.field.isValid()){
9287             v = this.startValue;
9288             this.cancelEdit(true);
9289         }
9290         if(String(v) === String(this.startValue) && this.ignoreNoChange){
9291             this.editing = false;
9292             this.hide();
9293             return;
9294         }
9295         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9296             this.editing = false;
9297             if(this.updateEl && this.boundEl){
9298                 this.boundEl.update(v);
9299             }
9300             if(remainVisible !== true){
9301                 this.hide();
9302             }
9303             this.fireEvent("complete", this, v, this.startValue);
9304         }
9305     },
9306
9307     // private
9308     onShow : function(){
9309         this.el.show();
9310         if(this.hideEl !== false){
9311             this.boundEl.hide();
9312         }
9313         this.field.show();
9314         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9315             this.fixIEFocus = true;
9316             this.deferredFocus.defer(50, this);
9317         }else{
9318             this.field.focus();
9319         }
9320         this.fireEvent("startedit", this.boundEl, this.startValue);
9321     },
9322
9323     deferredFocus : function(){
9324         if(this.editing){
9325             this.field.focus();
9326         }
9327     },
9328
9329     /**
9330      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
9331      * reverted to the original starting value.
9332      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9333      * cancel (defaults to false)
9334      */
9335     cancelEdit : function(remainVisible){
9336         if(this.editing){
9337             this.setValue(this.startValue);
9338             if(remainVisible !== true){
9339                 this.hide();
9340             }
9341         }
9342     },
9343
9344     // private
9345     onBlur : function(){
9346         if(this.allowBlur !== true && this.editing){
9347             this.completeEdit();
9348         }
9349     },
9350
9351     // private
9352     onHide : function(){
9353         if(this.editing){
9354             this.completeEdit();
9355             return;
9356         }
9357         this.field.blur();
9358         if(this.field.collapse){
9359             this.field.collapse();
9360         }
9361         this.el.hide();
9362         if(this.hideEl !== false){
9363             this.boundEl.show();
9364         }
9365         if(Roo.QuickTips){
9366             Roo.QuickTips.enable();
9367         }
9368     },
9369
9370     /**
9371      * Sets the data value of the editor
9372      * @param {Mixed} value Any valid value supported by the underlying field
9373      */
9374     setValue : function(v){
9375         this.field.setValue(v);
9376     },
9377
9378     /**
9379      * Gets the data value of the editor
9380      * @return {Mixed} The data value
9381      */
9382     getValue : function(){
9383         return this.field.getValue();
9384     }
9385 });/*
9386  * Based on:
9387  * Ext JS Library 1.1.1
9388  * Copyright(c) 2006-2007, Ext JS, LLC.
9389  *
9390  * Originally Released Under LGPL - original licence link has changed is not relivant.
9391  *
9392  * Fork - LGPL
9393  * <script type="text/javascript">
9394  */
9395  
9396 /**
9397  * @class Roo.BasicDialog
9398  * @extends Roo.util.Observable
9399  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9400  * <pre><code>
9401 var dlg = new Roo.BasicDialog("my-dlg", {
9402     height: 200,
9403     width: 300,
9404     minHeight: 100,
9405     minWidth: 150,
9406     modal: true,
9407     proxyDrag: true,
9408     shadow: true
9409 });
9410 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9411 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9412 dlg.addButton('Cancel', dlg.hide, dlg);
9413 dlg.show();
9414 </code></pre>
9415   <b>A Dialog should always be a direct child of the body element.</b>
9416  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9417  * @cfg {String} title Default text to display in the title bar (defaults to null)
9418  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9419  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9420  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9421  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9422  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9423  * (defaults to null with no animation)
9424  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9425  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9426  * property for valid values (defaults to 'all')
9427  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9428  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9429  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9430  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9431  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9432  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9433  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9434  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9435  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9436  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9437  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9438  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9439  * draggable = true (defaults to false)
9440  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9441  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9442  * shadow (defaults to false)
9443  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9444  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9445  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9446  * @cfg {Array} buttons Array of buttons
9447  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9448  * @constructor
9449  * Create a new BasicDialog.
9450  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9451  * @param {Object} config Configuration options
9452  */
9453 Roo.BasicDialog = function(el, config){
9454     this.el = Roo.get(el);
9455     var dh = Roo.DomHelper;
9456     if(!this.el && config && config.autoCreate){
9457         if(typeof config.autoCreate == "object"){
9458             if(!config.autoCreate.id){
9459                 config.autoCreate.id = el;
9460             }
9461             this.el = dh.append(document.body,
9462                         config.autoCreate, true);
9463         }else{
9464             this.el = dh.append(document.body,
9465                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9466         }
9467     }
9468     el = this.el;
9469     el.setDisplayed(true);
9470     el.hide = this.hideAction;
9471     this.id = el.id;
9472     el.addClass("x-dlg");
9473
9474     Roo.apply(this, config);
9475
9476     this.proxy = el.createProxy("x-dlg-proxy");
9477     this.proxy.hide = this.hideAction;
9478     this.proxy.setOpacity(.5);
9479     this.proxy.hide();
9480
9481     if(config.width){
9482         el.setWidth(config.width);
9483     }
9484     if(config.height){
9485         el.setHeight(config.height);
9486     }
9487     this.size = el.getSize();
9488     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9489         this.xy = [config.x,config.y];
9490     }else{
9491         this.xy = el.getCenterXY(true);
9492     }
9493     /** The header element @type Roo.Element */
9494     this.header = el.child("> .x-dlg-hd");
9495     /** The body element @type Roo.Element */
9496     this.body = el.child("> .x-dlg-bd");
9497     /** The footer element @type Roo.Element */
9498     this.footer = el.child("> .x-dlg-ft");
9499
9500     if(!this.header){
9501         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9502     }
9503     if(!this.body){
9504         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9505     }
9506
9507     this.header.unselectable();
9508     if(this.title){
9509         this.header.update(this.title);
9510     }
9511     // this element allows the dialog to be focused for keyboard event
9512     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9513     this.focusEl.swallowEvent("click", true);
9514
9515     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9516
9517     // wrap the body and footer for special rendering
9518     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9519     if(this.footer){
9520         this.bwrap.dom.appendChild(this.footer.dom);
9521     }
9522
9523     this.bg = this.el.createChild({
9524         tag: "div", cls:"x-dlg-bg",
9525         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9526     });
9527     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9528
9529
9530     if(this.autoScroll !== false && !this.autoTabs){
9531         this.body.setStyle("overflow", "auto");
9532     }
9533
9534     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9535
9536     if(this.closable !== false){
9537         this.el.addClass("x-dlg-closable");
9538         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9539         this.close.on("click", this.closeClick, this);
9540         this.close.addClassOnOver("x-dlg-close-over");
9541     }
9542     if(this.collapsible !== false){
9543         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9544         this.collapseBtn.on("click", this.collapseClick, this);
9545         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9546         this.header.on("dblclick", this.collapseClick, this);
9547     }
9548     if(this.resizable !== false){
9549         this.el.addClass("x-dlg-resizable");
9550         this.resizer = new Roo.Resizable(el, {
9551             minWidth: this.minWidth || 80,
9552             minHeight:this.minHeight || 80,
9553             handles: this.resizeHandles || "all",
9554             pinned: true
9555         });
9556         this.resizer.on("beforeresize", this.beforeResize, this);
9557         this.resizer.on("resize", this.onResize, this);
9558     }
9559     if(this.draggable !== false){
9560         el.addClass("x-dlg-draggable");
9561         if (!this.proxyDrag) {
9562             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9563         }
9564         else {
9565             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9566         }
9567         dd.setHandleElId(this.header.id);
9568         dd.endDrag = this.endMove.createDelegate(this);
9569         dd.startDrag = this.startMove.createDelegate(this);
9570         dd.onDrag = this.onDrag.createDelegate(this);
9571         dd.scroll = false;
9572         this.dd = dd;
9573     }
9574     if(this.modal){
9575         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9576         this.mask.enableDisplayMode("block");
9577         this.mask.hide();
9578         this.el.addClass("x-dlg-modal");
9579     }
9580     if(this.shadow){
9581         this.shadow = new Roo.Shadow({
9582             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9583             offset : this.shadowOffset
9584         });
9585     }else{
9586         this.shadowOffset = 0;
9587     }
9588     if(Roo.useShims && this.shim !== false){
9589         this.shim = this.el.createShim();
9590         this.shim.hide = this.hideAction;
9591         this.shim.hide();
9592     }else{
9593         this.shim = false;
9594     }
9595     if(this.autoTabs){
9596         this.initTabs();
9597     }
9598     if (this.buttons) { 
9599         var bts= this.buttons;
9600         this.buttons = [];
9601         Roo.each(bts, function(b) {
9602             this.addButton(b);
9603         }, this);
9604     }
9605     
9606     
9607     this.addEvents({
9608         /**
9609          * @event keydown
9610          * Fires when a key is pressed
9611          * @param {Roo.BasicDialog} this
9612          * @param {Roo.EventObject} e
9613          */
9614         "keydown" : true,
9615         /**
9616          * @event move
9617          * Fires when this dialog is moved by the user.
9618          * @param {Roo.BasicDialog} this
9619          * @param {Number} x The new page X
9620          * @param {Number} y The new page Y
9621          */
9622         "move" : true,
9623         /**
9624          * @event resize
9625          * Fires when this dialog is resized by the user.
9626          * @param {Roo.BasicDialog} this
9627          * @param {Number} width The new width
9628          * @param {Number} height The new height
9629          */
9630         "resize" : true,
9631         /**
9632          * @event beforehide
9633          * Fires before this dialog is hidden.
9634          * @param {Roo.BasicDialog} this
9635          */
9636         "beforehide" : true,
9637         /**
9638          * @event hide
9639          * Fires when this dialog is hidden.
9640          * @param {Roo.BasicDialog} this
9641          */
9642         "hide" : true,
9643         /**
9644          * @event beforeshow
9645          * Fires before this dialog is shown.
9646          * @param {Roo.BasicDialog} this
9647          */
9648         "beforeshow" : true,
9649         /**
9650          * @event show
9651          * Fires when this dialog is shown.
9652          * @param {Roo.BasicDialog} this
9653          */
9654         "show" : true
9655     });
9656     el.on("keydown", this.onKeyDown, this);
9657     el.on("mousedown", this.toFront, this);
9658     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9659     this.el.hide();
9660     Roo.DialogManager.register(this);
9661     Roo.BasicDialog.superclass.constructor.call(this);
9662 };
9663
9664 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9665     shadowOffset: Roo.isIE ? 6 : 5,
9666     minHeight: 80,
9667     minWidth: 200,
9668     minButtonWidth: 75,
9669     defaultButton: null,
9670     buttonAlign: "right",
9671     tabTag: 'div',
9672     firstShow: true,
9673
9674     /**
9675      * Sets the dialog title text
9676      * @param {String} text The title text to display
9677      * @return {Roo.BasicDialog} this
9678      */
9679     setTitle : function(text){
9680         this.header.update(text);
9681         return this;
9682     },
9683
9684     // private
9685     closeClick : function(){
9686         this.hide();
9687     },
9688
9689     // private
9690     collapseClick : function(){
9691         this[this.collapsed ? "expand" : "collapse"]();
9692     },
9693
9694     /**
9695      * Collapses the dialog to its minimized state (only the title bar is visible).
9696      * Equivalent to the user clicking the collapse dialog button.
9697      */
9698     collapse : function(){
9699         if(!this.collapsed){
9700             this.collapsed = true;
9701             this.el.addClass("x-dlg-collapsed");
9702             this.restoreHeight = this.el.getHeight();
9703             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9704         }
9705     },
9706
9707     /**
9708      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9709      * clicking the expand dialog button.
9710      */
9711     expand : function(){
9712         if(this.collapsed){
9713             this.collapsed = false;
9714             this.el.removeClass("x-dlg-collapsed");
9715             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9716         }
9717     },
9718
9719     /**
9720      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9721      * @return {Roo.TabPanel} The tabs component
9722      */
9723     initTabs : function(){
9724         var tabs = this.getTabs();
9725         while(tabs.getTab(0)){
9726             tabs.removeTab(0);
9727         }
9728         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9729             var dom = el.dom;
9730             tabs.addTab(Roo.id(dom), dom.title);
9731             dom.title = "";
9732         });
9733         tabs.activate(0);
9734         return tabs;
9735     },
9736
9737     // private
9738     beforeResize : function(){
9739         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9740     },
9741
9742     // private
9743     onResize : function(){
9744         this.refreshSize();
9745         this.syncBodyHeight();
9746         this.adjustAssets();
9747         this.focus();
9748         this.fireEvent("resize", this, this.size.width, this.size.height);
9749     },
9750
9751     // private
9752     onKeyDown : function(e){
9753         if(this.isVisible()){
9754             this.fireEvent("keydown", this, e);
9755         }
9756     },
9757
9758     /**
9759      * Resizes the dialog.
9760      * @param {Number} width
9761      * @param {Number} height
9762      * @return {Roo.BasicDialog} this
9763      */
9764     resizeTo : function(width, height){
9765         this.el.setSize(width, height);
9766         this.size = {width: width, height: height};
9767         this.syncBodyHeight();
9768         if(this.fixedcenter){
9769             this.center();
9770         }
9771         if(this.isVisible()){
9772             this.constrainXY();
9773             this.adjustAssets();
9774         }
9775         this.fireEvent("resize", this, width, height);
9776         return this;
9777     },
9778
9779
9780     /**
9781      * Resizes the dialog to fit the specified content size.
9782      * @param {Number} width
9783      * @param {Number} height
9784      * @return {Roo.BasicDialog} this
9785      */
9786     setContentSize : function(w, h){
9787         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9788         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9789         //if(!this.el.isBorderBox()){
9790             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9791             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9792         //}
9793         if(this.tabs){
9794             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9795             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9796         }
9797         this.resizeTo(w, h);
9798         return this;
9799     },
9800
9801     /**
9802      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9803      * executed in response to a particular key being pressed while the dialog is active.
9804      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9805      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9806      * @param {Function} fn The function to call
9807      * @param {Object} scope (optional) The scope of the function
9808      * @return {Roo.BasicDialog} this
9809      */
9810     addKeyListener : function(key, fn, scope){
9811         var keyCode, shift, ctrl, alt;
9812         if(typeof key == "object" && !(key instanceof Array)){
9813             keyCode = key["key"];
9814             shift = key["shift"];
9815             ctrl = key["ctrl"];
9816             alt = key["alt"];
9817         }else{
9818             keyCode = key;
9819         }
9820         var handler = function(dlg, e){
9821             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9822                 var k = e.getKey();
9823                 if(keyCode instanceof Array){
9824                     for(var i = 0, len = keyCode.length; i < len; i++){
9825                         if(keyCode[i] == k){
9826                           fn.call(scope || window, dlg, k, e);
9827                           return;
9828                         }
9829                     }
9830                 }else{
9831                     if(k == keyCode){
9832                         fn.call(scope || window, dlg, k, e);
9833                     }
9834                 }
9835             }
9836         };
9837         this.on("keydown", handler);
9838         return this;
9839     },
9840
9841     /**
9842      * Returns the TabPanel component (creates it if it doesn't exist).
9843      * Note: If you wish to simply check for the existence of tabs without creating them,
9844      * check for a null 'tabs' property.
9845      * @return {Roo.TabPanel} The tabs component
9846      */
9847     getTabs : function(){
9848         if(!this.tabs){
9849             this.el.addClass("x-dlg-auto-tabs");
9850             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9851             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9852         }
9853         return this.tabs;
9854     },
9855
9856     /**
9857      * Adds a button to the footer section of the dialog.
9858      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9859      * object or a valid Roo.DomHelper element config
9860      * @param {Function} handler The function called when the button is clicked
9861      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9862      * @return {Roo.Button} The new button
9863      */
9864     addButton : function(config, handler, scope){
9865         var dh = Roo.DomHelper;
9866         if(!this.footer){
9867             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9868         }
9869         if(!this.btnContainer){
9870             var tb = this.footer.createChild({
9871
9872                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9873                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9874             }, null, true);
9875             this.btnContainer = tb.firstChild.firstChild.firstChild;
9876         }
9877         var bconfig = {
9878             handler: handler,
9879             scope: scope,
9880             minWidth: this.minButtonWidth,
9881             hideParent:true
9882         };
9883         if(typeof config == "string"){
9884             bconfig.text = config;
9885         }else{
9886             if(config.tag){
9887                 bconfig.dhconfig = config;
9888             }else{
9889                 Roo.apply(bconfig, config);
9890             }
9891         }
9892         var fc = false;
9893         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9894             bconfig.position = Math.max(0, bconfig.position);
9895             fc = this.btnContainer.childNodes[bconfig.position];
9896         }
9897          
9898         var btn = new Roo.Button(
9899             fc ? 
9900                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9901                 : this.btnContainer.appendChild(document.createElement("td")),
9902             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9903             bconfig
9904         );
9905         this.syncBodyHeight();
9906         if(!this.buttons){
9907             /**
9908              * Array of all the buttons that have been added to this dialog via addButton
9909              * @type Array
9910              */
9911             this.buttons = [];
9912         }
9913         this.buttons.push(btn);
9914         return btn;
9915     },
9916
9917     /**
9918      * Sets the default button to be focused when the dialog is displayed.
9919      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9920      * @return {Roo.BasicDialog} this
9921      */
9922     setDefaultButton : function(btn){
9923         this.defaultButton = btn;
9924         return this;
9925     },
9926
9927     // private
9928     getHeaderFooterHeight : function(safe){
9929         var height = 0;
9930         if(this.header){
9931            height += this.header.getHeight();
9932         }
9933         if(this.footer){
9934            var fm = this.footer.getMargins();
9935             height += (this.footer.getHeight()+fm.top+fm.bottom);
9936         }
9937         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9938         height += this.centerBg.getPadding("tb");
9939         return height;
9940     },
9941
9942     // private
9943     syncBodyHeight : function()
9944     {
9945         var bd = this.body, // the text
9946             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9947             bw = this.bwrap;
9948         var height = this.size.height - this.getHeaderFooterHeight(false);
9949         bd.setHeight(height-bd.getMargins("tb"));
9950         var hh = this.header.getHeight();
9951         var h = this.size.height-hh;
9952         cb.setHeight(h);
9953         
9954         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9955         bw.setHeight(h-cb.getPadding("tb"));
9956         
9957         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9958         bd.setWidth(bw.getWidth(true));
9959         if(this.tabs){
9960             this.tabs.syncHeight();
9961             if(Roo.isIE){
9962                 this.tabs.el.repaint();
9963             }
9964         }
9965     },
9966
9967     /**
9968      * Restores the previous state of the dialog if Roo.state is configured.
9969      * @return {Roo.BasicDialog} this
9970      */
9971     restoreState : function(){
9972         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9973         if(box && box.width){
9974             this.xy = [box.x, box.y];
9975             this.resizeTo(box.width, box.height);
9976         }
9977         return this;
9978     },
9979
9980     // private
9981     beforeShow : function(){
9982         this.expand();
9983         if(this.fixedcenter){
9984             this.xy = this.el.getCenterXY(true);
9985         }
9986         if(this.modal){
9987             Roo.get(document.body).addClass("x-body-masked");
9988             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9989             this.mask.show();
9990         }
9991         this.constrainXY();
9992     },
9993
9994     // private
9995     animShow : function(){
9996         var b = Roo.get(this.animateTarget).getBox();
9997         this.proxy.setSize(b.width, b.height);
9998         this.proxy.setLocation(b.x, b.y);
9999         this.proxy.show();
10000         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
10001                     true, .35, this.showEl.createDelegate(this));
10002     },
10003
10004     /**
10005      * Shows the dialog.
10006      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
10007      * @return {Roo.BasicDialog} this
10008      */
10009     show : function(animateTarget){
10010         if (this.fireEvent("beforeshow", this) === false){
10011             return;
10012         }
10013         if(this.syncHeightBeforeShow){
10014             this.syncBodyHeight();
10015         }else if(this.firstShow){
10016             this.firstShow = false;
10017             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
10018         }
10019         this.animateTarget = animateTarget || this.animateTarget;
10020         if(!this.el.isVisible()){
10021             this.beforeShow();
10022             if(this.animateTarget && Roo.get(this.animateTarget)){
10023                 this.animShow();
10024             }else{
10025                 this.showEl();
10026             }
10027         }
10028         return this;
10029     },
10030
10031     // private
10032     showEl : function(){
10033         this.proxy.hide();
10034         this.el.setXY(this.xy);
10035         this.el.show();
10036         this.adjustAssets(true);
10037         this.toFront();
10038         this.focus();
10039         // IE peekaboo bug - fix found by Dave Fenwick
10040         if(Roo.isIE){
10041             this.el.repaint();
10042         }
10043         this.fireEvent("show", this);
10044     },
10045
10046     /**
10047      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
10048      * dialog itself will receive focus.
10049      */
10050     focus : function(){
10051         if(this.defaultButton){
10052             this.defaultButton.focus();
10053         }else{
10054             this.focusEl.focus();
10055         }
10056     },
10057
10058     // private
10059     constrainXY : function(){
10060         if(this.constraintoviewport !== false){
10061             if(!this.viewSize){
10062                 if(this.container){
10063                     var s = this.container.getSize();
10064                     this.viewSize = [s.width, s.height];
10065                 }else{
10066                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10067                 }
10068             }
10069             var s = Roo.get(this.container||document).getScroll();
10070
10071             var x = this.xy[0], y = this.xy[1];
10072             var w = this.size.width, h = this.size.height;
10073             var vw = this.viewSize[0], vh = this.viewSize[1];
10074             // only move it if it needs it
10075             var moved = false;
10076             // first validate right/bottom
10077             if(x + w > vw+s.left){
10078                 x = vw - w;
10079                 moved = true;
10080             }
10081             if(y + h > vh+s.top){
10082                 y = vh - h;
10083                 moved = true;
10084             }
10085             // then make sure top/left isn't negative
10086             if(x < s.left){
10087                 x = s.left;
10088                 moved = true;
10089             }
10090             if(y < s.top){
10091                 y = s.top;
10092                 moved = true;
10093             }
10094             if(moved){
10095                 // cache xy
10096                 this.xy = [x, y];
10097                 if(this.isVisible()){
10098                     this.el.setLocation(x, y);
10099                     this.adjustAssets();
10100                 }
10101             }
10102         }
10103     },
10104
10105     // private
10106     onDrag : function(){
10107         if(!this.proxyDrag){
10108             this.xy = this.el.getXY();
10109             this.adjustAssets();
10110         }
10111     },
10112
10113     // private
10114     adjustAssets : function(doShow){
10115         var x = this.xy[0], y = this.xy[1];
10116         var w = this.size.width, h = this.size.height;
10117         if(doShow === true){
10118             if(this.shadow){
10119                 this.shadow.show(this.el);
10120             }
10121             if(this.shim){
10122                 this.shim.show();
10123             }
10124         }
10125         if(this.shadow && this.shadow.isVisible()){
10126             this.shadow.show(this.el);
10127         }
10128         if(this.shim && this.shim.isVisible()){
10129             this.shim.setBounds(x, y, w, h);
10130         }
10131     },
10132
10133     // private
10134     adjustViewport : function(w, h){
10135         if(!w || !h){
10136             w = Roo.lib.Dom.getViewWidth();
10137             h = Roo.lib.Dom.getViewHeight();
10138         }
10139         // cache the size
10140         this.viewSize = [w, h];
10141         if(this.modal && this.mask.isVisible()){
10142             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10143             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10144         }
10145         if(this.isVisible()){
10146             this.constrainXY();
10147         }
10148     },
10149
10150     /**
10151      * Destroys this dialog and all its supporting elements (including any tabs, shim,
10152      * shadow, proxy, mask, etc.)  Also removes all event listeners.
10153      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10154      */
10155     destroy : function(removeEl){
10156         if(this.isVisible()){
10157             this.animateTarget = null;
10158             this.hide();
10159         }
10160         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10161         if(this.tabs){
10162             this.tabs.destroy(removeEl);
10163         }
10164         Roo.destroy(
10165              this.shim,
10166              this.proxy,
10167              this.resizer,
10168              this.close,
10169              this.mask
10170         );
10171         if(this.dd){
10172             this.dd.unreg();
10173         }
10174         if(this.buttons){
10175            for(var i = 0, len = this.buttons.length; i < len; i++){
10176                this.buttons[i].destroy();
10177            }
10178         }
10179         this.el.removeAllListeners();
10180         if(removeEl === true){
10181             this.el.update("");
10182             this.el.remove();
10183         }
10184         Roo.DialogManager.unregister(this);
10185     },
10186
10187     // private
10188     startMove : function(){
10189         if(this.proxyDrag){
10190             this.proxy.show();
10191         }
10192         if(this.constraintoviewport !== false){
10193             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10194         }
10195     },
10196
10197     // private
10198     endMove : function(){
10199         if(!this.proxyDrag){
10200             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10201         }else{
10202             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10203             this.proxy.hide();
10204         }
10205         this.refreshSize();
10206         this.adjustAssets();
10207         this.focus();
10208         this.fireEvent("move", this, this.xy[0], this.xy[1]);
10209     },
10210
10211     /**
10212      * Brings this dialog to the front of any other visible dialogs
10213      * @return {Roo.BasicDialog} this
10214      */
10215     toFront : function(){
10216         Roo.DialogManager.bringToFront(this);
10217         return this;
10218     },
10219
10220     /**
10221      * Sends this dialog to the back (under) of any other visible dialogs
10222      * @return {Roo.BasicDialog} this
10223      */
10224     toBack : function(){
10225         Roo.DialogManager.sendToBack(this);
10226         return this;
10227     },
10228
10229     /**
10230      * Centers this dialog in the viewport
10231      * @return {Roo.BasicDialog} this
10232      */
10233     center : function(){
10234         var xy = this.el.getCenterXY(true);
10235         this.moveTo(xy[0], xy[1]);
10236         return this;
10237     },
10238
10239     /**
10240      * Moves the dialog's top-left corner to the specified point
10241      * @param {Number} x
10242      * @param {Number} y
10243      * @return {Roo.BasicDialog} this
10244      */
10245     moveTo : function(x, y){
10246         this.xy = [x,y];
10247         if(this.isVisible()){
10248             this.el.setXY(this.xy);
10249             this.adjustAssets();
10250         }
10251         return this;
10252     },
10253
10254     /**
10255      * Aligns the dialog to the specified element
10256      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10257      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10258      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10259      * @return {Roo.BasicDialog} this
10260      */
10261     alignTo : function(element, position, offsets){
10262         this.xy = this.el.getAlignToXY(element, position, offsets);
10263         if(this.isVisible()){
10264             this.el.setXY(this.xy);
10265             this.adjustAssets();
10266         }
10267         return this;
10268     },
10269
10270     /**
10271      * Anchors an element to another element and realigns it when the window is resized.
10272      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10273      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10274      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10275      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10276      * is a number, it is used as the buffer delay (defaults to 50ms).
10277      * @return {Roo.BasicDialog} this
10278      */
10279     anchorTo : function(el, alignment, offsets, monitorScroll){
10280         var action = function(){
10281             this.alignTo(el, alignment, offsets);
10282         };
10283         Roo.EventManager.onWindowResize(action, this);
10284         var tm = typeof monitorScroll;
10285         if(tm != 'undefined'){
10286             Roo.EventManager.on(window, 'scroll', action, this,
10287                 {buffer: tm == 'number' ? monitorScroll : 50});
10288         }
10289         action.call(this);
10290         return this;
10291     },
10292
10293     /**
10294      * Returns true if the dialog is visible
10295      * @return {Boolean}
10296      */
10297     isVisible : function(){
10298         return this.el.isVisible();
10299     },
10300
10301     // private
10302     animHide : function(callback){
10303         var b = Roo.get(this.animateTarget).getBox();
10304         this.proxy.show();
10305         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10306         this.el.hide();
10307         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10308                     this.hideEl.createDelegate(this, [callback]));
10309     },
10310
10311     /**
10312      * Hides the dialog.
10313      * @param {Function} callback (optional) Function to call when the dialog is hidden
10314      * @return {Roo.BasicDialog} this
10315      */
10316     hide : function(callback){
10317         if (this.fireEvent("beforehide", this) === false){
10318             return;
10319         }
10320         if(this.shadow){
10321             this.shadow.hide();
10322         }
10323         if(this.shim) {
10324           this.shim.hide();
10325         }
10326         // sometimes animateTarget seems to get set.. causing problems...
10327         // this just double checks..
10328         if(this.animateTarget && Roo.get(this.animateTarget)) {
10329            this.animHide(callback);
10330         }else{
10331             this.el.hide();
10332             this.hideEl(callback);
10333         }
10334         return this;
10335     },
10336
10337     // private
10338     hideEl : function(callback){
10339         this.proxy.hide();
10340         if(this.modal){
10341             this.mask.hide();
10342             Roo.get(document.body).removeClass("x-body-masked");
10343         }
10344         this.fireEvent("hide", this);
10345         if(typeof callback == "function"){
10346             callback();
10347         }
10348     },
10349
10350     // private
10351     hideAction : function(){
10352         this.setLeft("-10000px");
10353         this.setTop("-10000px");
10354         this.setStyle("visibility", "hidden");
10355     },
10356
10357     // private
10358     refreshSize : function(){
10359         this.size = this.el.getSize();
10360         this.xy = this.el.getXY();
10361         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10362     },
10363
10364     // private
10365     // z-index is managed by the DialogManager and may be overwritten at any time
10366     setZIndex : function(index){
10367         if(this.modal){
10368             this.mask.setStyle("z-index", index);
10369         }
10370         if(this.shim){
10371             this.shim.setStyle("z-index", ++index);
10372         }
10373         if(this.shadow){
10374             this.shadow.setZIndex(++index);
10375         }
10376         this.el.setStyle("z-index", ++index);
10377         if(this.proxy){
10378             this.proxy.setStyle("z-index", ++index);
10379         }
10380         if(this.resizer){
10381             this.resizer.proxy.setStyle("z-index", ++index);
10382         }
10383
10384         this.lastZIndex = index;
10385     },
10386
10387     /**
10388      * Returns the element for this dialog
10389      * @return {Roo.Element} The underlying dialog Element
10390      */
10391     getEl : function(){
10392         return this.el;
10393     }
10394 });
10395
10396 /**
10397  * @class Roo.DialogManager
10398  * Provides global access to BasicDialogs that have been created and
10399  * support for z-indexing (layering) multiple open dialogs.
10400  */
10401 Roo.DialogManager = function(){
10402     var list = {};
10403     var accessList = [];
10404     var front = null;
10405
10406     // private
10407     var sortDialogs = function(d1, d2){
10408         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10409     };
10410
10411     // private
10412     var orderDialogs = function(){
10413         accessList.sort(sortDialogs);
10414         var seed = Roo.DialogManager.zseed;
10415         for(var i = 0, len = accessList.length; i < len; i++){
10416             var dlg = accessList[i];
10417             if(dlg){
10418                 dlg.setZIndex(seed + (i*10));
10419             }
10420         }
10421     };
10422
10423     return {
10424         /**
10425          * The starting z-index for BasicDialogs (defaults to 9000)
10426          * @type Number The z-index value
10427          */
10428         zseed : 9000,
10429
10430         // private
10431         register : function(dlg){
10432             list[dlg.id] = dlg;
10433             accessList.push(dlg);
10434         },
10435
10436         // private
10437         unregister : function(dlg){
10438             delete list[dlg.id];
10439             var i=0;
10440             var len=0;
10441             if(!accessList.indexOf){
10442                 for(  i = 0, len = accessList.length; i < len; i++){
10443                     if(accessList[i] == dlg){
10444                         accessList.splice(i, 1);
10445                         return;
10446                     }
10447                 }
10448             }else{
10449                  i = accessList.indexOf(dlg);
10450                 if(i != -1){
10451                     accessList.splice(i, 1);
10452                 }
10453             }
10454         },
10455
10456         /**
10457          * Gets a registered dialog by id
10458          * @param {String/Object} id The id of the dialog or a dialog
10459          * @return {Roo.BasicDialog} this
10460          */
10461         get : function(id){
10462             return typeof id == "object" ? id : list[id];
10463         },
10464
10465         /**
10466          * Brings the specified dialog to the front
10467          * @param {String/Object} dlg The id of the dialog or a dialog
10468          * @return {Roo.BasicDialog} this
10469          */
10470         bringToFront : function(dlg){
10471             dlg = this.get(dlg);
10472             if(dlg != front){
10473                 front = dlg;
10474                 dlg._lastAccess = new Date().getTime();
10475                 orderDialogs();
10476             }
10477             return dlg;
10478         },
10479
10480         /**
10481          * Sends the specified dialog to the back
10482          * @param {String/Object} dlg The id of the dialog or a dialog
10483          * @return {Roo.BasicDialog} this
10484          */
10485         sendToBack : function(dlg){
10486             dlg = this.get(dlg);
10487             dlg._lastAccess = -(new Date().getTime());
10488             orderDialogs();
10489             return dlg;
10490         },
10491
10492         /**
10493          * Hides all dialogs
10494          */
10495         hideAll : function(){
10496             for(var id in list){
10497                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10498                     list[id].hide();
10499                 }
10500             }
10501         }
10502     };
10503 }();
10504
10505 /**
10506  * @class Roo.LayoutDialog
10507  * @extends Roo.BasicDialog
10508  * Dialog which provides adjustments for working with a layout in a Dialog.
10509  * Add your necessary layout config options to the dialog's config.<br>
10510  * Example usage (including a nested layout):
10511  * <pre><code>
10512 if(!dialog){
10513     dialog = new Roo.LayoutDialog("download-dlg", {
10514         modal: true,
10515         width:600,
10516         height:450,
10517         shadow:true,
10518         minWidth:500,
10519         minHeight:350,
10520         autoTabs:true,
10521         proxyDrag:true,
10522         // layout config merges with the dialog config
10523         center:{
10524             tabPosition: "top",
10525             alwaysShowTabs: true
10526         }
10527     });
10528     dialog.addKeyListener(27, dialog.hide, dialog);
10529     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10530     dialog.addButton("Build It!", this.getDownload, this);
10531
10532     // we can even add nested layouts
10533     var innerLayout = new Roo.BorderLayout("dl-inner", {
10534         east: {
10535             initialSize: 200,
10536             autoScroll:true,
10537             split:true
10538         },
10539         center: {
10540             autoScroll:true
10541         }
10542     });
10543     innerLayout.beginUpdate();
10544     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10545     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10546     innerLayout.endUpdate(true);
10547
10548     var layout = dialog.getLayout();
10549     layout.beginUpdate();
10550     layout.add("center", new Roo.ContentPanel("standard-panel",
10551                         {title: "Download the Source", fitToFrame:true}));
10552     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10553                {title: "Build your own roo.js"}));
10554     layout.getRegion("center").showPanel(sp);
10555     layout.endUpdate();
10556 }
10557 </code></pre>
10558     * @constructor
10559     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10560     * @param {Object} config configuration options
10561   */
10562 Roo.LayoutDialog = function(el, cfg){
10563     
10564     var config=  cfg;
10565     if (typeof(cfg) == 'undefined') {
10566         config = Roo.apply({}, el);
10567         // not sure why we use documentElement here.. - it should always be body.
10568         // IE7 borks horribly if we use documentElement.
10569         // webkit also does not like documentElement - it creates a body element...
10570         el = Roo.get( document.body || document.documentElement ).createChild();
10571         //config.autoCreate = true;
10572     }
10573     
10574     
10575     config.autoTabs = false;
10576     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10577     this.body.setStyle({overflow:"hidden", position:"relative"});
10578     this.layout = new Roo.BorderLayout(this.body.dom, config);
10579     this.layout.monitorWindowResize = false;
10580     this.el.addClass("x-dlg-auto-layout");
10581     // fix case when center region overwrites center function
10582     this.center = Roo.BasicDialog.prototype.center;
10583     this.on("show", this.layout.layout, this.layout, true);
10584     if (config.items) {
10585         var xitems = config.items;
10586         delete config.items;
10587         Roo.each(xitems, this.addxtype, this);
10588     }
10589     
10590     
10591 };
10592 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10593     /**
10594      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10595      * @deprecated
10596      */
10597     endUpdate : function(){
10598         this.layout.endUpdate();
10599     },
10600
10601     /**
10602      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10603      *  @deprecated
10604      */
10605     beginUpdate : function(){
10606         this.layout.beginUpdate();
10607     },
10608
10609     /**
10610      * Get the BorderLayout for this dialog
10611      * @return {Roo.BorderLayout}
10612      */
10613     getLayout : function(){
10614         return this.layout;
10615     },
10616
10617     showEl : function(){
10618         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10619         if(Roo.isIE7){
10620             this.layout.layout();
10621         }
10622     },
10623
10624     // private
10625     // Use the syncHeightBeforeShow config option to control this automatically
10626     syncBodyHeight : function(){
10627         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10628         if(this.layout){this.layout.layout();}
10629     },
10630     
10631       /**
10632      * Add an xtype element (actually adds to the layout.)
10633      * @return {Object} xdata xtype object data.
10634      */
10635     
10636     addxtype : function(c) {
10637         return this.layout.addxtype(c);
10638     }
10639 });
10640
10641 Roo.apply(Roo.BasicDialog,  {
10642     lastZIndex : 0
10643 });/*
10644  * Based on:
10645  * Ext JS Library 1.1.1
10646  * Copyright(c) 2006-2007, Ext JS, LLC.
10647  *
10648  * Originally Released Under LGPL - original licence link has changed is not relivant.
10649  *
10650  * Fork - LGPL
10651  * <script type="text/javascript">
10652  */
10653  
10654 /**
10655  * @class Roo.MessageBox
10656  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10657  * Example usage:
10658  *<pre><code>
10659 // Basic alert:
10660 Roo.Msg.alert('Status', 'Changes saved successfully.');
10661
10662 // Prompt for user data:
10663 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10664     if (btn == 'ok'){
10665         // process text value...
10666     }
10667 });
10668
10669 // Show a dialog using config options:
10670 Roo.Msg.show({
10671    title:'Save Changes?',
10672    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10673    buttons: Roo.Msg.YESNOCANCEL,
10674    fn: processResult,
10675    animEl: 'elId'
10676 });
10677 </code></pre>
10678  * @singleton
10679  */
10680 Roo.MessageBox = function(){
10681     var dlg, opt, mask, waitTimer;
10682     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10683     var buttons, activeTextEl, bwidth;
10684
10685     // private
10686     var handleButton = function(button){
10687         dlg.hide();
10688         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10689     };
10690
10691     // private
10692     var handleHide = function(){
10693         if(opt && opt.cls){
10694             dlg.el.removeClass(opt.cls);
10695         }
10696         if(waitTimer){
10697             Roo.TaskMgr.stop(waitTimer);
10698             waitTimer = null;
10699         }
10700     };
10701
10702     // private
10703     var updateButtons = function(b){
10704         var width = 0;
10705         if(!b){
10706             buttons["ok"].hide();
10707             buttons["cancel"].hide();
10708             buttons["yes"].hide();
10709             buttons["no"].hide();
10710             dlg.footer.dom.style.display = 'none';
10711             return width;
10712         }
10713         dlg.footer.dom.style.display = '';
10714         for(var k in buttons){
10715             if(typeof buttons[k] != "function"){
10716                 if(b[k]){
10717                     buttons[k].show();
10718                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10719                     width += buttons[k].el.getWidth()+15;
10720                 }else{
10721                     buttons[k].hide();
10722                 }
10723             }
10724         }
10725         return width;
10726     };
10727
10728     // private
10729     var handleEsc = function(d, k, e){
10730         if(opt && opt.closable !== false){
10731             dlg.hide();
10732         }
10733         if(e){
10734             e.stopEvent();
10735         }
10736     };
10737
10738     return {
10739         /**
10740          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10741          * @return {Roo.BasicDialog} The BasicDialog element
10742          */
10743         getDialog : function(){
10744            if(!dlg){
10745                 dlg = new Roo.BasicDialog("x-msg-box", {
10746                     autoCreate : true,
10747                     shadow: true,
10748                     draggable: true,
10749                     resizable:false,
10750                     constraintoviewport:false,
10751                     fixedcenter:true,
10752                     collapsible : false,
10753                     shim:true,
10754                     modal: true,
10755                     width:400, height:100,
10756                     buttonAlign:"center",
10757                     closeClick : function(){
10758                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10759                             handleButton("no");
10760                         }else{
10761                             handleButton("cancel");
10762                         }
10763                     }
10764                 });
10765                 dlg.on("hide", handleHide);
10766                 mask = dlg.mask;
10767                 dlg.addKeyListener(27, handleEsc);
10768                 buttons = {};
10769                 var bt = this.buttonText;
10770                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10771                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10772                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10773                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10774                 bodyEl = dlg.body.createChild({
10775
10776                     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>'
10777                 });
10778                 msgEl = bodyEl.dom.firstChild;
10779                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10780                 textboxEl.enableDisplayMode();
10781                 textboxEl.addKeyListener([10,13], function(){
10782                     if(dlg.isVisible() && opt && opt.buttons){
10783                         if(opt.buttons.ok){
10784                             handleButton("ok");
10785                         }else if(opt.buttons.yes){
10786                             handleButton("yes");
10787                         }
10788                     }
10789                 });
10790                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10791                 textareaEl.enableDisplayMode();
10792                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10793                 progressEl.enableDisplayMode();
10794                 var pf = progressEl.dom.firstChild;
10795                 if (pf) {
10796                     pp = Roo.get(pf.firstChild);
10797                     pp.setHeight(pf.offsetHeight);
10798                 }
10799                 
10800             }
10801             return dlg;
10802         },
10803
10804         /**
10805          * Updates the message box body text
10806          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10807          * the XHTML-compliant non-breaking space character '&amp;#160;')
10808          * @return {Roo.MessageBox} This message box
10809          */
10810         updateText : function(text){
10811             if(!dlg.isVisible() && !opt.width){
10812                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10813             }
10814             msgEl.innerHTML = text || '&#160;';
10815       
10816             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10817             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10818             var w = Math.max(
10819                     Math.min(opt.width || cw , this.maxWidth), 
10820                     Math.max(opt.minWidth || this.minWidth, bwidth)
10821             );
10822             if(opt.prompt){
10823                 activeTextEl.setWidth(w);
10824             }
10825             if(dlg.isVisible()){
10826                 dlg.fixedcenter = false;
10827             }
10828             // to big, make it scroll. = But as usual stupid IE does not support
10829             // !important..
10830             
10831             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10832                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10833                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10834             } else {
10835                 bodyEl.dom.style.height = '';
10836                 bodyEl.dom.style.overflowY = '';
10837             }
10838             if (cw > w) {
10839                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10840             } else {
10841                 bodyEl.dom.style.overflowX = '';
10842             }
10843             
10844             dlg.setContentSize(w, bodyEl.getHeight());
10845             if(dlg.isVisible()){
10846                 dlg.fixedcenter = true;
10847             }
10848             return this;
10849         },
10850
10851         /**
10852          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10853          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10854          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10855          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10856          * @return {Roo.MessageBox} This message box
10857          */
10858         updateProgress : function(value, text){
10859             if(text){
10860                 this.updateText(text);
10861             }
10862             if (pp) { // weird bug on my firefox - for some reason this is not defined
10863                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10864             }
10865             return this;
10866         },        
10867
10868         /**
10869          * Returns true if the message box is currently displayed
10870          * @return {Boolean} True if the message box is visible, else false
10871          */
10872         isVisible : function(){
10873             return dlg && dlg.isVisible();  
10874         },
10875
10876         /**
10877          * Hides the message box if it is displayed
10878          */
10879         hide : function(){
10880             if(this.isVisible()){
10881                 dlg.hide();
10882             }  
10883         },
10884
10885         /**
10886          * Displays a new message box, or reinitializes an existing message box, based on the config options
10887          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10888          * The following config object properties are supported:
10889          * <pre>
10890 Property    Type             Description
10891 ----------  ---------------  ------------------------------------------------------------------------------------
10892 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10893                                    closes (defaults to undefined)
10894 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10895                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10896 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10897                                    progress and wait dialogs will ignore this property and always hide the
10898                                    close button as they can only be closed programmatically.
10899 cls               String           A custom CSS class to apply to the message box element
10900 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10901                                    displayed (defaults to 75)
10902 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10903                                    function will be btn (the name of the button that was clicked, if applicable,
10904                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10905                                    Progress and wait dialogs will ignore this option since they do not respond to
10906                                    user actions and can only be closed programmatically, so any required function
10907                                    should be called by the same code after it closes the dialog.
10908 icon              String           A CSS class that provides a background image to be used as an icon for
10909                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10910 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10911 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10912 modal             Boolean          False to allow user interaction with the page while the message box is
10913                                    displayed (defaults to true)
10914 msg               String           A string that will replace the existing message box body text (defaults
10915                                    to the XHTML-compliant non-breaking space character '&#160;')
10916 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10917 progress          Boolean          True to display a progress bar (defaults to false)
10918 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10919 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10920 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10921 title             String           The title text
10922 value             String           The string value to set into the active textbox element if displayed
10923 wait              Boolean          True to display a progress bar (defaults to false)
10924 width             Number           The width of the dialog in pixels
10925 </pre>
10926          *
10927          * Example usage:
10928          * <pre><code>
10929 Roo.Msg.show({
10930    title: 'Address',
10931    msg: 'Please enter your address:',
10932    width: 300,
10933    buttons: Roo.MessageBox.OKCANCEL,
10934    multiline: true,
10935    fn: saveAddress,
10936    animEl: 'addAddressBtn'
10937 });
10938 </code></pre>
10939          * @param {Object} config Configuration options
10940          * @return {Roo.MessageBox} This message box
10941          */
10942         show : function(options)
10943         {
10944             
10945             // this causes nightmares if you show one dialog after another
10946             // especially on callbacks..
10947              
10948             if(this.isVisible()){
10949                 
10950                 this.hide();
10951                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10952                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10953                 Roo.log("New Dialog Message:" +  options.msg )
10954                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10955                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10956                 
10957             }
10958             var d = this.getDialog();
10959             opt = options;
10960             d.setTitle(opt.title || "&#160;");
10961             d.close.setDisplayed(opt.closable !== false);
10962             activeTextEl = textboxEl;
10963             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10964             if(opt.prompt){
10965                 if(opt.multiline){
10966                     textboxEl.hide();
10967                     textareaEl.show();
10968                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10969                         opt.multiline : this.defaultTextHeight);
10970                     activeTextEl = textareaEl;
10971                 }else{
10972                     textboxEl.show();
10973                     textareaEl.hide();
10974                 }
10975             }else{
10976                 textboxEl.hide();
10977                 textareaEl.hide();
10978             }
10979             progressEl.setDisplayed(opt.progress === true);
10980             this.updateProgress(0);
10981             activeTextEl.dom.value = opt.value || "";
10982             if(opt.prompt){
10983                 dlg.setDefaultButton(activeTextEl);
10984             }else{
10985                 var bs = opt.buttons;
10986                 var db = null;
10987                 if(bs && bs.ok){
10988                     db = buttons["ok"];
10989                 }else if(bs && bs.yes){
10990                     db = buttons["yes"];
10991                 }
10992                 dlg.setDefaultButton(db);
10993             }
10994             bwidth = updateButtons(opt.buttons);
10995             this.updateText(opt.msg);
10996             if(opt.cls){
10997                 d.el.addClass(opt.cls);
10998             }
10999             d.proxyDrag = opt.proxyDrag === true;
11000             d.modal = opt.modal !== false;
11001             d.mask = opt.modal !== false ? mask : false;
11002             if(!d.isVisible()){
11003                 // force it to the end of the z-index stack so it gets a cursor in FF
11004                 document.body.appendChild(dlg.el.dom);
11005                 d.animateTarget = null;
11006                 d.show(options.animEl);
11007             }
11008             return this;
11009         },
11010
11011         /**
11012          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
11013          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
11014          * and closing the message box when the process is complete.
11015          * @param {String} title The title bar text
11016          * @param {String} msg The message box body text
11017          * @return {Roo.MessageBox} This message box
11018          */
11019         progress : function(title, msg){
11020             this.show({
11021                 title : title,
11022                 msg : msg,
11023                 buttons: false,
11024                 progress:true,
11025                 closable:false,
11026                 minWidth: this.minProgressWidth,
11027                 modal : true
11028             });
11029             return this;
11030         },
11031
11032         /**
11033          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11034          * If a callback function is passed it will be called after the user clicks the button, and the
11035          * id of the button that was clicked will be passed as the only parameter to the callback
11036          * (could also be the top-right close button).
11037          * @param {String} title The title bar text
11038          * @param {String} msg The message box body text
11039          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11040          * @param {Object} scope (optional) The scope of the callback function
11041          * @return {Roo.MessageBox} This message box
11042          */
11043         alert : function(title, msg, fn, scope){
11044             this.show({
11045                 title : title,
11046                 msg : msg,
11047                 buttons: this.OK,
11048                 fn: fn,
11049                 scope : scope,
11050                 modal : true
11051             });
11052             return this;
11053         },
11054
11055         /**
11056          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
11057          * interaction while waiting for a long-running process to complete that does not have defined intervals.
11058          * You are responsible for closing the message box when the process is complete.
11059          * @param {String} msg The message box body text
11060          * @param {String} title (optional) The title bar text
11061          * @return {Roo.MessageBox} This message box
11062          */
11063         wait : function(msg, title){
11064             this.show({
11065                 title : title,
11066                 msg : msg,
11067                 buttons: false,
11068                 closable:false,
11069                 progress:true,
11070                 modal:true,
11071                 width:300,
11072                 wait:true
11073             });
11074             waitTimer = Roo.TaskMgr.start({
11075                 run: function(i){
11076                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11077                 },
11078                 interval: 1000
11079             });
11080             return this;
11081         },
11082
11083         /**
11084          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11085          * If a callback function is passed it will be called after the user clicks either button, and the id of the
11086          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11087          * @param {String} title The title bar text
11088          * @param {String} msg The message box body text
11089          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11090          * @param {Object} scope (optional) The scope of the callback function
11091          * @return {Roo.MessageBox} This message box
11092          */
11093         confirm : function(title, msg, fn, scope){
11094             this.show({
11095                 title : title,
11096                 msg : msg,
11097                 buttons: this.YESNO,
11098                 fn: fn,
11099                 scope : scope,
11100                 modal : true
11101             });
11102             return this;
11103         },
11104
11105         /**
11106          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11107          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
11108          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11109          * (could also be the top-right close button) and the text that was entered will be passed as the two
11110          * parameters to the callback.
11111          * @param {String} title The title bar text
11112          * @param {String} msg The message box body text
11113          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11114          * @param {Object} scope (optional) The scope of the callback function
11115          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11116          * property, or the height in pixels to create the textbox (defaults to false / single-line)
11117          * @return {Roo.MessageBox} This message box
11118          */
11119         prompt : function(title, msg, fn, scope, multiline){
11120             this.show({
11121                 title : title,
11122                 msg : msg,
11123                 buttons: this.OKCANCEL,
11124                 fn: fn,
11125                 minWidth:250,
11126                 scope : scope,
11127                 prompt:true,
11128                 multiline: multiline,
11129                 modal : true
11130             });
11131             return this;
11132         },
11133
11134         /**
11135          * Button config that displays a single OK button
11136          * @type Object
11137          */
11138         OK : {ok:true},
11139         /**
11140          * Button config that displays Yes and No buttons
11141          * @type Object
11142          */
11143         YESNO : {yes:true, no:true},
11144         /**
11145          * Button config that displays OK and Cancel buttons
11146          * @type Object
11147          */
11148         OKCANCEL : {ok:true, cancel:true},
11149         /**
11150          * Button config that displays Yes, No and Cancel buttons
11151          * @type Object
11152          */
11153         YESNOCANCEL : {yes:true, no:true, cancel:true},
11154
11155         /**
11156          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11157          * @type Number
11158          */
11159         defaultTextHeight : 75,
11160         /**
11161          * The maximum width in pixels of the message box (defaults to 600)
11162          * @type Number
11163          */
11164         maxWidth : 600,
11165         /**
11166          * The minimum width in pixels of the message box (defaults to 100)
11167          * @type Number
11168          */
11169         minWidth : 100,
11170         /**
11171          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
11172          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11173          * @type Number
11174          */
11175         minProgressWidth : 250,
11176         /**
11177          * An object containing the default button text strings that can be overriden for localized language support.
11178          * Supported properties are: ok, cancel, yes and no.
11179          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11180          * @type Object
11181          */
11182         buttonText : {
11183             ok : "OK",
11184             cancel : "Cancel",
11185             yes : "Yes",
11186             no : "No"
11187         }
11188     };
11189 }();
11190
11191 /**
11192  * Shorthand for {@link Roo.MessageBox}
11193  */
11194 Roo.Msg = Roo.MessageBox;/*
11195  * Based on:
11196  * Ext JS Library 1.1.1
11197  * Copyright(c) 2006-2007, Ext JS, LLC.
11198  *
11199  * Originally Released Under LGPL - original licence link has changed is not relivant.
11200  *
11201  * Fork - LGPL
11202  * <script type="text/javascript">
11203  */
11204 /**
11205  * @class Roo.QuickTips
11206  * Provides attractive and customizable tooltips for any element.
11207  * @singleton
11208  */
11209 Roo.QuickTips = function(){
11210     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11211     var ce, bd, xy, dd;
11212     var visible = false, disabled = true, inited = false;
11213     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11214     
11215     var onOver = function(e){
11216         if(disabled){
11217             return;
11218         }
11219         var t = e.getTarget();
11220         if(!t || t.nodeType !== 1 || t == document || t == document.body){
11221             return;
11222         }
11223         if(ce && t == ce.el){
11224             clearTimeout(hideProc);
11225             return;
11226         }
11227         if(t && tagEls[t.id]){
11228             tagEls[t.id].el = t;
11229             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11230             return;
11231         }
11232         var ttp, et = Roo.fly(t);
11233         var ns = cfg.namespace;
11234         if(tm.interceptTitles && t.title){
11235             ttp = t.title;
11236             t.qtip = ttp;
11237             t.removeAttribute("title");
11238             e.preventDefault();
11239         }else{
11240             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11241         }
11242         if(ttp){
11243             showProc = show.defer(tm.showDelay, tm, [{
11244                 el: t, 
11245                 text: ttp.replace(/\\n/g,'<br/>'),
11246                 width: et.getAttributeNS(ns, cfg.width),
11247                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11248                 title: et.getAttributeNS(ns, cfg.title),
11249                     cls: et.getAttributeNS(ns, cfg.cls)
11250             }]);
11251         }
11252     };
11253     
11254     var onOut = function(e){
11255         clearTimeout(showProc);
11256         var t = e.getTarget();
11257         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11258             hideProc = setTimeout(hide, tm.hideDelay);
11259         }
11260     };
11261     
11262     var onMove = function(e){
11263         if(disabled){
11264             return;
11265         }
11266         xy = e.getXY();
11267         xy[1] += 18;
11268         if(tm.trackMouse && ce){
11269             el.setXY(xy);
11270         }
11271     };
11272     
11273     var onDown = function(e){
11274         clearTimeout(showProc);
11275         clearTimeout(hideProc);
11276         if(!e.within(el)){
11277             if(tm.hideOnClick){
11278                 hide();
11279                 tm.disable();
11280                 tm.enable.defer(100, tm);
11281             }
11282         }
11283     };
11284     
11285     var getPad = function(){
11286         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11287     };
11288
11289     var show = function(o){
11290         if(disabled){
11291             return;
11292         }
11293         clearTimeout(dismissProc);
11294         ce = o;
11295         if(removeCls){ // in case manually hidden
11296             el.removeClass(removeCls);
11297             removeCls = null;
11298         }
11299         if(ce.cls){
11300             el.addClass(ce.cls);
11301             removeCls = ce.cls;
11302         }
11303         if(ce.title){
11304             tipTitle.update(ce.title);
11305             tipTitle.show();
11306         }else{
11307             tipTitle.update('');
11308             tipTitle.hide();
11309         }
11310         el.dom.style.width  = tm.maxWidth+'px';
11311         //tipBody.dom.style.width = '';
11312         tipBodyText.update(o.text);
11313         var p = getPad(), w = ce.width;
11314         if(!w){
11315             var td = tipBodyText.dom;
11316             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11317             if(aw > tm.maxWidth){
11318                 w = tm.maxWidth;
11319             }else if(aw < tm.minWidth){
11320                 w = tm.minWidth;
11321             }else{
11322                 w = aw;
11323             }
11324         }
11325         //tipBody.setWidth(w);
11326         el.setWidth(parseInt(w, 10) + p);
11327         if(ce.autoHide === false){
11328             close.setDisplayed(true);
11329             if(dd){
11330                 dd.unlock();
11331             }
11332         }else{
11333             close.setDisplayed(false);
11334             if(dd){
11335                 dd.lock();
11336             }
11337         }
11338         if(xy){
11339             el.avoidY = xy[1]-18;
11340             el.setXY(xy);
11341         }
11342         if(tm.animate){
11343             el.setOpacity(.1);
11344             el.setStyle("visibility", "visible");
11345             el.fadeIn({callback: afterShow});
11346         }else{
11347             afterShow();
11348         }
11349     };
11350     
11351     var afterShow = function(){
11352         if(ce){
11353             el.show();
11354             esc.enable();
11355             if(tm.autoDismiss && ce.autoHide !== false){
11356                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11357             }
11358         }
11359     };
11360     
11361     var hide = function(noanim){
11362         clearTimeout(dismissProc);
11363         clearTimeout(hideProc);
11364         ce = null;
11365         if(el.isVisible()){
11366             esc.disable();
11367             if(noanim !== true && tm.animate){
11368                 el.fadeOut({callback: afterHide});
11369             }else{
11370                 afterHide();
11371             } 
11372         }
11373     };
11374     
11375     var afterHide = function(){
11376         el.hide();
11377         if(removeCls){
11378             el.removeClass(removeCls);
11379             removeCls = null;
11380         }
11381     };
11382     
11383     return {
11384         /**
11385         * @cfg {Number} minWidth
11386         * The minimum width of the quick tip (defaults to 40)
11387         */
11388        minWidth : 40,
11389         /**
11390         * @cfg {Number} maxWidth
11391         * The maximum width of the quick tip (defaults to 300)
11392         */
11393        maxWidth : 300,
11394         /**
11395         * @cfg {Boolean} interceptTitles
11396         * True to automatically use the element's DOM title value if available (defaults to false)
11397         */
11398        interceptTitles : false,
11399         /**
11400         * @cfg {Boolean} trackMouse
11401         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11402         */
11403        trackMouse : false,
11404         /**
11405         * @cfg {Boolean} hideOnClick
11406         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11407         */
11408        hideOnClick : true,
11409         /**
11410         * @cfg {Number} showDelay
11411         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11412         */
11413        showDelay : 500,
11414         /**
11415         * @cfg {Number} hideDelay
11416         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11417         */
11418        hideDelay : 200,
11419         /**
11420         * @cfg {Boolean} autoHide
11421         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11422         * Used in conjunction with hideDelay.
11423         */
11424        autoHide : true,
11425         /**
11426         * @cfg {Boolean}
11427         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11428         * (defaults to true).  Used in conjunction with autoDismissDelay.
11429         */
11430        autoDismiss : true,
11431         /**
11432         * @cfg {Number}
11433         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11434         */
11435        autoDismissDelay : 5000,
11436        /**
11437         * @cfg {Boolean} animate
11438         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11439         */
11440        animate : false,
11441
11442        /**
11443         * @cfg {String} title
11444         * Title text to display (defaults to '').  This can be any valid HTML markup.
11445         */
11446         title: '',
11447        /**
11448         * @cfg {String} text
11449         * Body text to display (defaults to '').  This can be any valid HTML markup.
11450         */
11451         text : '',
11452        /**
11453         * @cfg {String} cls
11454         * A CSS class to apply to the base quick tip element (defaults to '').
11455         */
11456         cls : '',
11457        /**
11458         * @cfg {Number} width
11459         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11460         * minWidth or maxWidth.
11461         */
11462         width : null,
11463
11464     /**
11465      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11466      * or display QuickTips in a page.
11467      */
11468        init : function(){
11469           tm = Roo.QuickTips;
11470           cfg = tm.tagConfig;
11471           if(!inited){
11472               if(!Roo.isReady){ // allow calling of init() before onReady
11473                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11474                   return;
11475               }
11476               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11477               el.fxDefaults = {stopFx: true};
11478               // maximum custom styling
11479               //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>');
11480               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>');              
11481               tipTitle = el.child('h3');
11482               tipTitle.enableDisplayMode("block");
11483               tipBody = el.child('div.x-tip-bd');
11484               tipBodyText = el.child('div.x-tip-bd-inner');
11485               //bdLeft = el.child('div.x-tip-bd-left');
11486               //bdRight = el.child('div.x-tip-bd-right');
11487               close = el.child('div.x-tip-close');
11488               close.enableDisplayMode("block");
11489               close.on("click", hide);
11490               var d = Roo.get(document);
11491               d.on("mousedown", onDown);
11492               d.on("mouseover", onOver);
11493               d.on("mouseout", onOut);
11494               d.on("mousemove", onMove);
11495               esc = d.addKeyListener(27, hide);
11496               esc.disable();
11497               if(Roo.dd.DD){
11498                   dd = el.initDD("default", null, {
11499                       onDrag : function(){
11500                           el.sync();  
11501                       }
11502                   });
11503                   dd.setHandleElId(tipTitle.id);
11504                   dd.lock();
11505               }
11506               inited = true;
11507           }
11508           this.enable(); 
11509        },
11510
11511     /**
11512      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11513      * are supported:
11514      * <pre>
11515 Property    Type                   Description
11516 ----------  ---------------------  ------------------------------------------------------------------------
11517 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11518      * </ul>
11519      * @param {Object} config The config object
11520      */
11521        register : function(config){
11522            var cs = config instanceof Array ? config : arguments;
11523            for(var i = 0, len = cs.length; i < len; i++) {
11524                var c = cs[i];
11525                var target = c.target;
11526                if(target){
11527                    if(target instanceof Array){
11528                        for(var j = 0, jlen = target.length; j < jlen; j++){
11529                            tagEls[target[j]] = c;
11530                        }
11531                    }else{
11532                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11533                    }
11534                }
11535            }
11536        },
11537
11538     /**
11539      * Removes this quick tip from its element and destroys it.
11540      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11541      */
11542        unregister : function(el){
11543            delete tagEls[Roo.id(el)];
11544        },
11545
11546     /**
11547      * Enable this quick tip.
11548      */
11549        enable : function(){
11550            if(inited && disabled){
11551                locks.pop();
11552                if(locks.length < 1){
11553                    disabled = false;
11554                }
11555            }
11556        },
11557
11558     /**
11559      * Disable this quick tip.
11560      */
11561        disable : function(){
11562           disabled = true;
11563           clearTimeout(showProc);
11564           clearTimeout(hideProc);
11565           clearTimeout(dismissProc);
11566           if(ce){
11567               hide(true);
11568           }
11569           locks.push(1);
11570        },
11571
11572     /**
11573      * Returns true if the quick tip is enabled, else false.
11574      */
11575        isEnabled : function(){
11576             return !disabled;
11577        },
11578
11579         // private
11580        tagConfig : {
11581            namespace : "roo", // was ext?? this may break..
11582            alt_namespace : "ext",
11583            attribute : "qtip",
11584            width : "width",
11585            target : "target",
11586            title : "qtitle",
11587            hide : "hide",
11588            cls : "qclass"
11589        }
11590    };
11591 }();
11592
11593 // backwards compat
11594 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11595  * Based on:
11596  * Ext JS Library 1.1.1
11597  * Copyright(c) 2006-2007, Ext JS, LLC.
11598  *
11599  * Originally Released Under LGPL - original licence link has changed is not relivant.
11600  *
11601  * Fork - LGPL
11602  * <script type="text/javascript">
11603  */
11604  
11605
11606 /**
11607  * @class Roo.tree.TreePanel
11608  * @extends Roo.data.Tree
11609
11610  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11611  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11612  * @cfg {Boolean} enableDD true to enable drag and drop
11613  * @cfg {Boolean} enableDrag true to enable just drag
11614  * @cfg {Boolean} enableDrop true to enable just drop
11615  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11616  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11617  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11618  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11619  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11620  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11621  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11622  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11623  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11624  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11625  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11626  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11627  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11628  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11629  * @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>
11630  * @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>
11631  * 
11632  * @constructor
11633  * @param {String/HTMLElement/Element} el The container element
11634  * @param {Object} config
11635  */
11636 Roo.tree.TreePanel = function(el, config){
11637     var root = false;
11638     var loader = false;
11639     if (config.root) {
11640         root = config.root;
11641         delete config.root;
11642     }
11643     if (config.loader) {
11644         loader = config.loader;
11645         delete config.loader;
11646     }
11647     
11648     Roo.apply(this, config);
11649     Roo.tree.TreePanel.superclass.constructor.call(this);
11650     this.el = Roo.get(el);
11651     this.el.addClass('x-tree');
11652     //console.log(root);
11653     if (root) {
11654         this.setRootNode( Roo.factory(root, Roo.tree));
11655     }
11656     if (loader) {
11657         this.loader = Roo.factory(loader, Roo.tree);
11658     }
11659    /**
11660     * Read-only. The id of the container element becomes this TreePanel's id.
11661     */
11662     this.id = this.el.id;
11663     this.addEvents({
11664         /**
11665         * @event beforeload
11666         * Fires before a node is loaded, return false to cancel
11667         * @param {Node} node The node being loaded
11668         */
11669         "beforeload" : true,
11670         /**
11671         * @event load
11672         * Fires when a node is loaded
11673         * @param {Node} node The node that was loaded
11674         */
11675         "load" : true,
11676         /**
11677         * @event textchange
11678         * Fires when the text for a node is changed
11679         * @param {Node} node The node
11680         * @param {String} text The new text
11681         * @param {String} oldText The old text
11682         */
11683         "textchange" : true,
11684         /**
11685         * @event beforeexpand
11686         * Fires before a node is expanded, return false to cancel.
11687         * @param {Node} node The node
11688         * @param {Boolean} deep
11689         * @param {Boolean} anim
11690         */
11691         "beforeexpand" : true,
11692         /**
11693         * @event beforecollapse
11694         * Fires before a node is collapsed, return false to cancel.
11695         * @param {Node} node The node
11696         * @param {Boolean} deep
11697         * @param {Boolean} anim
11698         */
11699         "beforecollapse" : true,
11700         /**
11701         * @event expand
11702         * Fires when a node is expanded
11703         * @param {Node} node The node
11704         */
11705         "expand" : true,
11706         /**
11707         * @event disabledchange
11708         * Fires when the disabled status of a node changes
11709         * @param {Node} node The node
11710         * @param {Boolean} disabled
11711         */
11712         "disabledchange" : true,
11713         /**
11714         * @event collapse
11715         * Fires when a node is collapsed
11716         * @param {Node} node The node
11717         */
11718         "collapse" : true,
11719         /**
11720         * @event beforeclick
11721         * Fires before click processing on a node. Return false to cancel the default action.
11722         * @param {Node} node The node
11723         * @param {Roo.EventObject} e The event object
11724         */
11725         "beforeclick":true,
11726         /**
11727         * @event checkchange
11728         * Fires when a node with a checkbox's checked property changes
11729         * @param {Node} this This node
11730         * @param {Boolean} checked
11731         */
11732         "checkchange":true,
11733         /**
11734         * @event click
11735         * Fires when a node is clicked
11736         * @param {Node} node The node
11737         * @param {Roo.EventObject} e The event object
11738         */
11739         "click":true,
11740         /**
11741         * @event dblclick
11742         * Fires when a node is double clicked
11743         * @param {Node} node The node
11744         * @param {Roo.EventObject} e The event object
11745         */
11746         "dblclick":true,
11747         /**
11748         * @event contextmenu
11749         * Fires when a node is right clicked
11750         * @param {Node} node The node
11751         * @param {Roo.EventObject} e The event object
11752         */
11753         "contextmenu":true,
11754         /**
11755         * @event beforechildrenrendered
11756         * Fires right before the child nodes for a node are rendered
11757         * @param {Node} node The node
11758         */
11759         "beforechildrenrendered":true,
11760         /**
11761         * @event startdrag
11762         * Fires when a node starts being dragged
11763         * @param {Roo.tree.TreePanel} this
11764         * @param {Roo.tree.TreeNode} node
11765         * @param {event} e The raw browser event
11766         */ 
11767        "startdrag" : true,
11768        /**
11769         * @event enddrag
11770         * Fires when a drag operation is complete
11771         * @param {Roo.tree.TreePanel} this
11772         * @param {Roo.tree.TreeNode} node
11773         * @param {event} e The raw browser event
11774         */
11775        "enddrag" : true,
11776        /**
11777         * @event dragdrop
11778         * Fires when a dragged node is dropped on a valid DD target
11779         * @param {Roo.tree.TreePanel} this
11780         * @param {Roo.tree.TreeNode} node
11781         * @param {DD} dd The dd it was dropped on
11782         * @param {event} e The raw browser event
11783         */
11784        "dragdrop" : true,
11785        /**
11786         * @event beforenodedrop
11787         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11788         * passed to handlers has the following properties:<br />
11789         * <ul style="padding:5px;padding-left:16px;">
11790         * <li>tree - The TreePanel</li>
11791         * <li>target - The node being targeted for the drop</li>
11792         * <li>data - The drag data from the drag source</li>
11793         * <li>point - The point of the drop - append, above or below</li>
11794         * <li>source - The drag source</li>
11795         * <li>rawEvent - Raw mouse event</li>
11796         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11797         * to be inserted by setting them on this object.</li>
11798         * <li>cancel - Set this to true to cancel the drop.</li>
11799         * </ul>
11800         * @param {Object} dropEvent
11801         */
11802        "beforenodedrop" : true,
11803        /**
11804         * @event nodedrop
11805         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11806         * passed to handlers has the following properties:<br />
11807         * <ul style="padding:5px;padding-left:16px;">
11808         * <li>tree - The TreePanel</li>
11809         * <li>target - The node being targeted for the drop</li>
11810         * <li>data - The drag data from the drag source</li>
11811         * <li>point - The point of the drop - append, above or below</li>
11812         * <li>source - The drag source</li>
11813         * <li>rawEvent - Raw mouse event</li>
11814         * <li>dropNode - Dropped node(s).</li>
11815         * </ul>
11816         * @param {Object} dropEvent
11817         */
11818        "nodedrop" : true,
11819         /**
11820         * @event nodedragover
11821         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11822         * passed to handlers has the following properties:<br />
11823         * <ul style="padding:5px;padding-left:16px;">
11824         * <li>tree - The TreePanel</li>
11825         * <li>target - The node being targeted for the drop</li>
11826         * <li>data - The drag data from the drag source</li>
11827         * <li>point - The point of the drop - append, above or below</li>
11828         * <li>source - The drag source</li>
11829         * <li>rawEvent - Raw mouse event</li>
11830         * <li>dropNode - Drop node(s) provided by the source.</li>
11831         * <li>cancel - Set this to true to signal drop not allowed.</li>
11832         * </ul>
11833         * @param {Object} dragOverEvent
11834         */
11835        "nodedragover" : true,
11836        /**
11837         * @event appendnode
11838         * Fires when append node to the tree
11839         * @param {Roo.tree.TreePanel} this
11840         * @param {Roo.tree.TreeNode} node
11841         * @param {Number} index The index of the newly appended node
11842         */
11843        "appendnode" : true
11844         
11845     });
11846     if(this.singleExpand){
11847        this.on("beforeexpand", this.restrictExpand, this);
11848     }
11849     if (this.editor) {
11850         this.editor.tree = this;
11851         this.editor = Roo.factory(this.editor, Roo.tree);
11852     }
11853     
11854     if (this.selModel) {
11855         this.selModel = Roo.factory(this.selModel, Roo.tree);
11856     }
11857    
11858 };
11859 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11860     rootVisible : true,
11861     animate: Roo.enableFx,
11862     lines : true,
11863     enableDD : false,
11864     hlDrop : Roo.enableFx,
11865   
11866     renderer: false,
11867     
11868     rendererTip: false,
11869     // private
11870     restrictExpand : function(node){
11871         var p = node.parentNode;
11872         if(p){
11873             if(p.expandedChild && p.expandedChild.parentNode == p){
11874                 p.expandedChild.collapse();
11875             }
11876             p.expandedChild = node;
11877         }
11878     },
11879
11880     // private override
11881     setRootNode : function(node){
11882         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11883         if(!this.rootVisible){
11884             node.ui = new Roo.tree.RootTreeNodeUI(node);
11885         }
11886         return node;
11887     },
11888
11889     /**
11890      * Returns the container element for this TreePanel
11891      */
11892     getEl : function(){
11893         return this.el;
11894     },
11895
11896     /**
11897      * Returns the default TreeLoader for this TreePanel
11898      */
11899     getLoader : function(){
11900         return this.loader;
11901     },
11902
11903     /**
11904      * Expand all nodes
11905      */
11906     expandAll : function(){
11907         this.root.expand(true);
11908     },
11909
11910     /**
11911      * Collapse all nodes
11912      */
11913     collapseAll : function(){
11914         this.root.collapse(true);
11915     },
11916
11917     /**
11918      * Returns the selection model used by this TreePanel
11919      */
11920     getSelectionModel : function(){
11921         if(!this.selModel){
11922             this.selModel = new Roo.tree.DefaultSelectionModel();
11923         }
11924         return this.selModel;
11925     },
11926
11927     /**
11928      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11929      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11930      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11931      * @return {Array}
11932      */
11933     getChecked : function(a, startNode){
11934         startNode = startNode || this.root;
11935         var r = [];
11936         var f = function(){
11937             if(this.attributes.checked){
11938                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11939             }
11940         }
11941         startNode.cascade(f);
11942         return r;
11943     },
11944
11945     /**
11946      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11947      * @param {String} path
11948      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11949      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11950      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11951      */
11952     expandPath : function(path, attr, callback){
11953         attr = attr || "id";
11954         var keys = path.split(this.pathSeparator);
11955         var curNode = this.root;
11956         if(curNode.attributes[attr] != keys[1]){ // invalid root
11957             if(callback){
11958                 callback(false, null);
11959             }
11960             return;
11961         }
11962         var index = 1;
11963         var f = function(){
11964             if(++index == keys.length){
11965                 if(callback){
11966                     callback(true, curNode);
11967                 }
11968                 return;
11969             }
11970             var c = curNode.findChild(attr, keys[index]);
11971             if(!c){
11972                 if(callback){
11973                     callback(false, curNode);
11974                 }
11975                 return;
11976             }
11977             curNode = c;
11978             c.expand(false, false, f);
11979         };
11980         curNode.expand(false, false, f);
11981     },
11982
11983     /**
11984      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11985      * @param {String} path
11986      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11987      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11988      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11989      */
11990     selectPath : function(path, attr, callback){
11991         attr = attr || "id";
11992         var keys = path.split(this.pathSeparator);
11993         var v = keys.pop();
11994         if(keys.length > 0){
11995             var f = function(success, node){
11996                 if(success && node){
11997                     var n = node.findChild(attr, v);
11998                     if(n){
11999                         n.select();
12000                         if(callback){
12001                             callback(true, n);
12002                         }
12003                     }else if(callback){
12004                         callback(false, n);
12005                     }
12006                 }else{
12007                     if(callback){
12008                         callback(false, n);
12009                     }
12010                 }
12011             };
12012             this.expandPath(keys.join(this.pathSeparator), attr, f);
12013         }else{
12014             this.root.select();
12015             if(callback){
12016                 callback(true, this.root);
12017             }
12018         }
12019     },
12020
12021     getTreeEl : function(){
12022         return this.el;
12023     },
12024
12025     /**
12026      * Trigger rendering of this TreePanel
12027      */
12028     render : function(){
12029         if (this.innerCt) {
12030             return this; // stop it rendering more than once!!
12031         }
12032         
12033         this.innerCt = this.el.createChild({tag:"ul",
12034                cls:"x-tree-root-ct " +
12035                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12036
12037         if(this.containerScroll){
12038             Roo.dd.ScrollManager.register(this.el);
12039         }
12040         if((this.enableDD || this.enableDrop) && !this.dropZone){
12041            /**
12042             * The dropZone used by this tree if drop is enabled
12043             * @type Roo.tree.TreeDropZone
12044             */
12045              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12046                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12047            });
12048         }
12049         if((this.enableDD || this.enableDrag) && !this.dragZone){
12050            /**
12051             * The dragZone used by this tree if drag is enabled
12052             * @type Roo.tree.TreeDragZone
12053             */
12054             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12055                ddGroup: this.ddGroup || "TreeDD",
12056                scroll: this.ddScroll
12057            });
12058         }
12059         this.getSelectionModel().init(this);
12060         if (!this.root) {
12061             Roo.log("ROOT not set in tree");
12062             return this;
12063         }
12064         this.root.render();
12065         if(!this.rootVisible){
12066             this.root.renderChildren();
12067         }
12068         return this;
12069     }
12070 });/*
12071  * Based on:
12072  * Ext JS Library 1.1.1
12073  * Copyright(c) 2006-2007, Ext JS, LLC.
12074  *
12075  * Originally Released Under LGPL - original licence link has changed is not relivant.
12076  *
12077  * Fork - LGPL
12078  * <script type="text/javascript">
12079  */
12080  
12081
12082 /**
12083  * @class Roo.tree.DefaultSelectionModel
12084  * @extends Roo.util.Observable
12085  * The default single selection for a TreePanel.
12086  * @param {Object} cfg Configuration
12087  */
12088 Roo.tree.DefaultSelectionModel = function(cfg){
12089    this.selNode = null;
12090    
12091    
12092    
12093    this.addEvents({
12094        /**
12095         * @event selectionchange
12096         * Fires when the selected node changes
12097         * @param {DefaultSelectionModel} this
12098         * @param {TreeNode} node the new selection
12099         */
12100        "selectionchange" : true,
12101
12102        /**
12103         * @event beforeselect
12104         * Fires before the selected node changes, return false to cancel the change
12105         * @param {DefaultSelectionModel} this
12106         * @param {TreeNode} node the new selection
12107         * @param {TreeNode} node the old selection
12108         */
12109        "beforeselect" : true
12110    });
12111    
12112     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12113 };
12114
12115 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12116     init : function(tree){
12117         this.tree = tree;
12118         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12119         tree.on("click", this.onNodeClick, this);
12120     },
12121     
12122     onNodeClick : function(node, e){
12123         if (e.ctrlKey && this.selNode == node)  {
12124             this.unselect(node);
12125             return;
12126         }
12127         this.select(node);
12128     },
12129     
12130     /**
12131      * Select a node.
12132      * @param {TreeNode} node The node to select
12133      * @return {TreeNode} The selected node
12134      */
12135     select : function(node){
12136         var last = this.selNode;
12137         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12138             if(last){
12139                 last.ui.onSelectedChange(false);
12140             }
12141             this.selNode = node;
12142             node.ui.onSelectedChange(true);
12143             this.fireEvent("selectionchange", this, node, last);
12144         }
12145         return node;
12146     },
12147     
12148     /**
12149      * Deselect a node.
12150      * @param {TreeNode} node The node to unselect
12151      */
12152     unselect : function(node){
12153         if(this.selNode == node){
12154             this.clearSelections();
12155         }    
12156     },
12157     
12158     /**
12159      * Clear all selections
12160      */
12161     clearSelections : function(){
12162         var n = this.selNode;
12163         if(n){
12164             n.ui.onSelectedChange(false);
12165             this.selNode = null;
12166             this.fireEvent("selectionchange", this, null);
12167         }
12168         return n;
12169     },
12170     
12171     /**
12172      * Get the selected node
12173      * @return {TreeNode} The selected node
12174      */
12175     getSelectedNode : function(){
12176         return this.selNode;    
12177     },
12178     
12179     /**
12180      * Returns true if the node is selected
12181      * @param {TreeNode} node The node to check
12182      * @return {Boolean}
12183      */
12184     isSelected : function(node){
12185         return this.selNode == node;  
12186     },
12187
12188     /**
12189      * Selects the node above the selected node in the tree, intelligently walking the nodes
12190      * @return TreeNode The new selection
12191      */
12192     selectPrevious : function(){
12193         var s = this.selNode || this.lastSelNode;
12194         if(!s){
12195             return null;
12196         }
12197         var ps = s.previousSibling;
12198         if(ps){
12199             if(!ps.isExpanded() || ps.childNodes.length < 1){
12200                 return this.select(ps);
12201             } else{
12202                 var lc = ps.lastChild;
12203                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12204                     lc = lc.lastChild;
12205                 }
12206                 return this.select(lc);
12207             }
12208         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12209             return this.select(s.parentNode);
12210         }
12211         return null;
12212     },
12213
12214     /**
12215      * Selects the node above the selected node in the tree, intelligently walking the nodes
12216      * @return TreeNode The new selection
12217      */
12218     selectNext : function(){
12219         var s = this.selNode || this.lastSelNode;
12220         if(!s){
12221             return null;
12222         }
12223         if(s.firstChild && s.isExpanded()){
12224              return this.select(s.firstChild);
12225          }else if(s.nextSibling){
12226              return this.select(s.nextSibling);
12227          }else if(s.parentNode){
12228             var newS = null;
12229             s.parentNode.bubble(function(){
12230                 if(this.nextSibling){
12231                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
12232                     return false;
12233                 }
12234             });
12235             return newS;
12236          }
12237         return null;
12238     },
12239
12240     onKeyDown : function(e){
12241         var s = this.selNode || this.lastSelNode;
12242         // undesirable, but required
12243         var sm = this;
12244         if(!s){
12245             return;
12246         }
12247         var k = e.getKey();
12248         switch(k){
12249              case e.DOWN:
12250                  e.stopEvent();
12251                  this.selectNext();
12252              break;
12253              case e.UP:
12254                  e.stopEvent();
12255                  this.selectPrevious();
12256              break;
12257              case e.RIGHT:
12258                  e.preventDefault();
12259                  if(s.hasChildNodes()){
12260                      if(!s.isExpanded()){
12261                          s.expand();
12262                      }else if(s.firstChild){
12263                          this.select(s.firstChild, e);
12264                      }
12265                  }
12266              break;
12267              case e.LEFT:
12268                  e.preventDefault();
12269                  if(s.hasChildNodes() && s.isExpanded()){
12270                      s.collapse();
12271                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12272                      this.select(s.parentNode, e);
12273                  }
12274              break;
12275         };
12276     }
12277 });
12278
12279 /**
12280  * @class Roo.tree.MultiSelectionModel
12281  * @extends Roo.util.Observable
12282  * Multi selection for a TreePanel.
12283  * @param {Object} cfg Configuration
12284  */
12285 Roo.tree.MultiSelectionModel = function(){
12286    this.selNodes = [];
12287    this.selMap = {};
12288    this.addEvents({
12289        /**
12290         * @event selectionchange
12291         * Fires when the selected nodes change
12292         * @param {MultiSelectionModel} this
12293         * @param {Array} nodes Array of the selected nodes
12294         */
12295        "selectionchange" : true
12296    });
12297    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12298    
12299 };
12300
12301 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12302     init : function(tree){
12303         this.tree = tree;
12304         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12305         tree.on("click", this.onNodeClick, this);
12306     },
12307     
12308     onNodeClick : function(node, e){
12309         this.select(node, e, e.ctrlKey);
12310     },
12311     
12312     /**
12313      * Select a node.
12314      * @param {TreeNode} node The node to select
12315      * @param {EventObject} e (optional) An event associated with the selection
12316      * @param {Boolean} keepExisting True to retain existing selections
12317      * @return {TreeNode} The selected node
12318      */
12319     select : function(node, e, keepExisting){
12320         if(keepExisting !== true){
12321             this.clearSelections(true);
12322         }
12323         if(this.isSelected(node)){
12324             this.lastSelNode = node;
12325             return node;
12326         }
12327         this.selNodes.push(node);
12328         this.selMap[node.id] = node;
12329         this.lastSelNode = node;
12330         node.ui.onSelectedChange(true);
12331         this.fireEvent("selectionchange", this, this.selNodes);
12332         return node;
12333     },
12334     
12335     /**
12336      * Deselect a node.
12337      * @param {TreeNode} node The node to unselect
12338      */
12339     unselect : function(node){
12340         if(this.selMap[node.id]){
12341             node.ui.onSelectedChange(false);
12342             var sn = this.selNodes;
12343             var index = -1;
12344             if(sn.indexOf){
12345                 index = sn.indexOf(node);
12346             }else{
12347                 for(var i = 0, len = sn.length; i < len; i++){
12348                     if(sn[i] == node){
12349                         index = i;
12350                         break;
12351                     }
12352                 }
12353             }
12354             if(index != -1){
12355                 this.selNodes.splice(index, 1);
12356             }
12357             delete this.selMap[node.id];
12358             this.fireEvent("selectionchange", this, this.selNodes);
12359         }
12360     },
12361     
12362     /**
12363      * Clear all selections
12364      */
12365     clearSelections : function(suppressEvent){
12366         var sn = this.selNodes;
12367         if(sn.length > 0){
12368             for(var i = 0, len = sn.length; i < len; i++){
12369                 sn[i].ui.onSelectedChange(false);
12370             }
12371             this.selNodes = [];
12372             this.selMap = {};
12373             if(suppressEvent !== true){
12374                 this.fireEvent("selectionchange", this, this.selNodes);
12375             }
12376         }
12377     },
12378     
12379     /**
12380      * Returns true if the node is selected
12381      * @param {TreeNode} node The node to check
12382      * @return {Boolean}
12383      */
12384     isSelected : function(node){
12385         return this.selMap[node.id] ? true : false;  
12386     },
12387     
12388     /**
12389      * Returns an array of the selected nodes
12390      * @return {Array}
12391      */
12392     getSelectedNodes : function(){
12393         return this.selNodes;    
12394     },
12395
12396     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12397
12398     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12399
12400     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12401 });/*
12402  * Based on:
12403  * Ext JS Library 1.1.1
12404  * Copyright(c) 2006-2007, Ext JS, LLC.
12405  *
12406  * Originally Released Under LGPL - original licence link has changed is not relivant.
12407  *
12408  * Fork - LGPL
12409  * <script type="text/javascript">
12410  */
12411  
12412 /**
12413  * @class Roo.tree.TreeNode
12414  * @extends Roo.data.Node
12415  * @cfg {String} text The text for this node
12416  * @cfg {Boolean} expanded true to start the node expanded
12417  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12418  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12419  * @cfg {Boolean} disabled true to start the node disabled
12420  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12421  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12422  * @cfg {String} cls A css class to be added to the node
12423  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12424  * @cfg {String} href URL of the link used for the node (defaults to #)
12425  * @cfg {String} hrefTarget target frame for the link
12426  * @cfg {String} qtip An Ext QuickTip for the node
12427  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12428  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12429  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12430  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12431  * (defaults to undefined with no checkbox rendered)
12432  * @constructor
12433  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12434  */
12435 Roo.tree.TreeNode = function(attributes){
12436     attributes = attributes || {};
12437     if(typeof attributes == "string"){
12438         attributes = {text: attributes};
12439     }
12440     this.childrenRendered = false;
12441     this.rendered = false;
12442     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12443     this.expanded = attributes.expanded === true;
12444     this.isTarget = attributes.isTarget !== false;
12445     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12446     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12447
12448     /**
12449      * Read-only. The text for this node. To change it use setText().
12450      * @type String
12451      */
12452     this.text = attributes.text;
12453     /**
12454      * True if this node is disabled.
12455      * @type Boolean
12456      */
12457     this.disabled = attributes.disabled === true;
12458
12459     this.addEvents({
12460         /**
12461         * @event textchange
12462         * Fires when the text for this node is changed
12463         * @param {Node} this This node
12464         * @param {String} text The new text
12465         * @param {String} oldText The old text
12466         */
12467         "textchange" : true,
12468         /**
12469         * @event beforeexpand
12470         * Fires before this node is expanded, return false to cancel.
12471         * @param {Node} this This node
12472         * @param {Boolean} deep
12473         * @param {Boolean} anim
12474         */
12475         "beforeexpand" : true,
12476         /**
12477         * @event beforecollapse
12478         * Fires before this node is collapsed, return false to cancel.
12479         * @param {Node} this This node
12480         * @param {Boolean} deep
12481         * @param {Boolean} anim
12482         */
12483         "beforecollapse" : true,
12484         /**
12485         * @event expand
12486         * Fires when this node is expanded
12487         * @param {Node} this This node
12488         */
12489         "expand" : true,
12490         /**
12491         * @event disabledchange
12492         * Fires when the disabled status of this node changes
12493         * @param {Node} this This node
12494         * @param {Boolean} disabled
12495         */
12496         "disabledchange" : true,
12497         /**
12498         * @event collapse
12499         * Fires when this node is collapsed
12500         * @param {Node} this This node
12501         */
12502         "collapse" : true,
12503         /**
12504         * @event beforeclick
12505         * Fires before click processing. Return false to cancel the default action.
12506         * @param {Node} this This node
12507         * @param {Roo.EventObject} e The event object
12508         */
12509         "beforeclick":true,
12510         /**
12511         * @event checkchange
12512         * Fires when a node with a checkbox's checked property changes
12513         * @param {Node} this This node
12514         * @param {Boolean} checked
12515         */
12516         "checkchange":true,
12517         /**
12518         * @event click
12519         * Fires when this node is clicked
12520         * @param {Node} this This node
12521         * @param {Roo.EventObject} e The event object
12522         */
12523         "click":true,
12524         /**
12525         * @event dblclick
12526         * Fires when this node is double clicked
12527         * @param {Node} this This node
12528         * @param {Roo.EventObject} e The event object
12529         */
12530         "dblclick":true,
12531         /**
12532         * @event contextmenu
12533         * Fires when this node is right clicked
12534         * @param {Node} this This node
12535         * @param {Roo.EventObject} e The event object
12536         */
12537         "contextmenu":true,
12538         /**
12539         * @event beforechildrenrendered
12540         * Fires right before the child nodes for this node are rendered
12541         * @param {Node} this This node
12542         */
12543         "beforechildrenrendered":true
12544     });
12545
12546     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12547
12548     /**
12549      * Read-only. The UI for this node
12550      * @type TreeNodeUI
12551      */
12552     this.ui = new uiClass(this);
12553     
12554     // finally support items[]
12555     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12556         return;
12557     }
12558     
12559     
12560     Roo.each(this.attributes.items, function(c) {
12561         this.appendChild(Roo.factory(c,Roo.Tree));
12562     }, this);
12563     delete this.attributes.items;
12564     
12565     
12566     
12567 };
12568 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12569     preventHScroll: true,
12570     /**
12571      * Returns true if this node is expanded
12572      * @return {Boolean}
12573      */
12574     isExpanded : function(){
12575         return this.expanded;
12576     },
12577
12578     /**
12579      * Returns the UI object for this node
12580      * @return {TreeNodeUI}
12581      */
12582     getUI : function(){
12583         return this.ui;
12584     },
12585
12586     // private override
12587     setFirstChild : function(node){
12588         var of = this.firstChild;
12589         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12590         if(this.childrenRendered && of && node != of){
12591             of.renderIndent(true, true);
12592         }
12593         if(this.rendered){
12594             this.renderIndent(true, true);
12595         }
12596     },
12597
12598     // private override
12599     setLastChild : function(node){
12600         var ol = this.lastChild;
12601         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12602         if(this.childrenRendered && ol && node != ol){
12603             ol.renderIndent(true, true);
12604         }
12605         if(this.rendered){
12606             this.renderIndent(true, true);
12607         }
12608     },
12609
12610     // these methods are overridden to provide lazy rendering support
12611     // private override
12612     appendChild : function()
12613     {
12614         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12615         if(node && this.childrenRendered){
12616             node.render();
12617         }
12618         this.ui.updateExpandIcon();
12619         return node;
12620     },
12621
12622     // private override
12623     removeChild : function(node){
12624         this.ownerTree.getSelectionModel().unselect(node);
12625         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12626         // if it's been rendered remove dom node
12627         if(this.childrenRendered){
12628             node.ui.remove();
12629         }
12630         if(this.childNodes.length < 1){
12631             this.collapse(false, false);
12632         }else{
12633             this.ui.updateExpandIcon();
12634         }
12635         if(!this.firstChild) {
12636             this.childrenRendered = false;
12637         }
12638         return node;
12639     },
12640
12641     // private override
12642     insertBefore : function(node, refNode){
12643         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12644         if(newNode && refNode && this.childrenRendered){
12645             node.render();
12646         }
12647         this.ui.updateExpandIcon();
12648         return newNode;
12649     },
12650
12651     /**
12652      * Sets the text for this node
12653      * @param {String} text
12654      */
12655     setText : function(text){
12656         var oldText = this.text;
12657         this.text = text;
12658         this.attributes.text = text;
12659         if(this.rendered){ // event without subscribing
12660             this.ui.onTextChange(this, text, oldText);
12661         }
12662         this.fireEvent("textchange", this, text, oldText);
12663     },
12664
12665     /**
12666      * Triggers selection of this node
12667      */
12668     select : function(){
12669         this.getOwnerTree().getSelectionModel().select(this);
12670     },
12671
12672     /**
12673      * Triggers deselection of this node
12674      */
12675     unselect : function(){
12676         this.getOwnerTree().getSelectionModel().unselect(this);
12677     },
12678
12679     /**
12680      * Returns true if this node is selected
12681      * @return {Boolean}
12682      */
12683     isSelected : function(){
12684         return this.getOwnerTree().getSelectionModel().isSelected(this);
12685     },
12686
12687     /**
12688      * Expand this node.
12689      * @param {Boolean} deep (optional) True to expand all children as well
12690      * @param {Boolean} anim (optional) false to cancel the default animation
12691      * @param {Function} callback (optional) A callback to be called when
12692      * expanding this node completes (does not wait for deep expand to complete).
12693      * Called with 1 parameter, this node.
12694      */
12695     expand : function(deep, anim, callback){
12696         if(!this.expanded){
12697             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12698                 return;
12699             }
12700             if(!this.childrenRendered){
12701                 this.renderChildren();
12702             }
12703             this.expanded = true;
12704             
12705             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12706                 this.ui.animExpand(function(){
12707                     this.fireEvent("expand", this);
12708                     if(typeof callback == "function"){
12709                         callback(this);
12710                     }
12711                     if(deep === true){
12712                         this.expandChildNodes(true);
12713                     }
12714                 }.createDelegate(this));
12715                 return;
12716             }else{
12717                 this.ui.expand();
12718                 this.fireEvent("expand", this);
12719                 if(typeof callback == "function"){
12720                     callback(this);
12721                 }
12722             }
12723         }else{
12724            if(typeof callback == "function"){
12725                callback(this);
12726            }
12727         }
12728         if(deep === true){
12729             this.expandChildNodes(true);
12730         }
12731     },
12732
12733     isHiddenRoot : function(){
12734         return this.isRoot && !this.getOwnerTree().rootVisible;
12735     },
12736
12737     /**
12738      * Collapse this node.
12739      * @param {Boolean} deep (optional) True to collapse all children as well
12740      * @param {Boolean} anim (optional) false to cancel the default animation
12741      */
12742     collapse : function(deep, anim){
12743         if(this.expanded && !this.isHiddenRoot()){
12744             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12745                 return;
12746             }
12747             this.expanded = false;
12748             if((this.getOwnerTree().animate && anim !== false) || anim){
12749                 this.ui.animCollapse(function(){
12750                     this.fireEvent("collapse", this);
12751                     if(deep === true){
12752                         this.collapseChildNodes(true);
12753                     }
12754                 }.createDelegate(this));
12755                 return;
12756             }else{
12757                 this.ui.collapse();
12758                 this.fireEvent("collapse", this);
12759             }
12760         }
12761         if(deep === true){
12762             var cs = this.childNodes;
12763             for(var i = 0, len = cs.length; i < len; i++) {
12764                 cs[i].collapse(true, false);
12765             }
12766         }
12767     },
12768
12769     // private
12770     delayedExpand : function(delay){
12771         if(!this.expandProcId){
12772             this.expandProcId = this.expand.defer(delay, this);
12773         }
12774     },
12775
12776     // private
12777     cancelExpand : function(){
12778         if(this.expandProcId){
12779             clearTimeout(this.expandProcId);
12780         }
12781         this.expandProcId = false;
12782     },
12783
12784     /**
12785      * Toggles expanded/collapsed state of the node
12786      */
12787     toggle : function(){
12788         if(this.expanded){
12789             this.collapse();
12790         }else{
12791             this.expand();
12792         }
12793     },
12794
12795     /**
12796      * Ensures all parent nodes are expanded
12797      */
12798     ensureVisible : function(callback){
12799         var tree = this.getOwnerTree();
12800         tree.expandPath(this.parentNode.getPath(), false, function(){
12801             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12802             Roo.callback(callback);
12803         }.createDelegate(this));
12804     },
12805
12806     /**
12807      * Expand all child nodes
12808      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12809      */
12810     expandChildNodes : function(deep){
12811         var cs = this.childNodes;
12812         for(var i = 0, len = cs.length; i < len; i++) {
12813                 cs[i].expand(deep);
12814         }
12815     },
12816
12817     /**
12818      * Collapse all child nodes
12819      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12820      */
12821     collapseChildNodes : function(deep){
12822         var cs = this.childNodes;
12823         for(var i = 0, len = cs.length; i < len; i++) {
12824                 cs[i].collapse(deep);
12825         }
12826     },
12827
12828     /**
12829      * Disables this node
12830      */
12831     disable : function(){
12832         this.disabled = true;
12833         this.unselect();
12834         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12835             this.ui.onDisableChange(this, true);
12836         }
12837         this.fireEvent("disabledchange", this, true);
12838     },
12839
12840     /**
12841      * Enables this node
12842      */
12843     enable : function(){
12844         this.disabled = false;
12845         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12846             this.ui.onDisableChange(this, false);
12847         }
12848         this.fireEvent("disabledchange", this, false);
12849     },
12850
12851     // private
12852     renderChildren : function(suppressEvent){
12853         if(suppressEvent !== false){
12854             this.fireEvent("beforechildrenrendered", this);
12855         }
12856         var cs = this.childNodes;
12857         for(var i = 0, len = cs.length; i < len; i++){
12858             cs[i].render(true);
12859         }
12860         this.childrenRendered = true;
12861     },
12862
12863     // private
12864     sort : function(fn, scope){
12865         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12866         if(this.childrenRendered){
12867             var cs = this.childNodes;
12868             for(var i = 0, len = cs.length; i < len; i++){
12869                 cs[i].render(true);
12870             }
12871         }
12872     },
12873
12874     // private
12875     render : function(bulkRender){
12876         this.ui.render(bulkRender);
12877         if(!this.rendered){
12878             this.rendered = true;
12879             if(this.expanded){
12880                 this.expanded = false;
12881                 this.expand(false, false);
12882             }
12883         }
12884     },
12885
12886     // private
12887     renderIndent : function(deep, refresh){
12888         if(refresh){
12889             this.ui.childIndent = null;
12890         }
12891         this.ui.renderIndent();
12892         if(deep === true && this.childrenRendered){
12893             var cs = this.childNodes;
12894             for(var i = 0, len = cs.length; i < len; i++){
12895                 cs[i].renderIndent(true, refresh);
12896             }
12897         }
12898     }
12899 });/*
12900  * Based on:
12901  * Ext JS Library 1.1.1
12902  * Copyright(c) 2006-2007, Ext JS, LLC.
12903  *
12904  * Originally Released Under LGPL - original licence link has changed is not relivant.
12905  *
12906  * Fork - LGPL
12907  * <script type="text/javascript">
12908  */
12909  
12910 /**
12911  * @class Roo.tree.AsyncTreeNode
12912  * @extends Roo.tree.TreeNode
12913  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12914  * @constructor
12915  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12916  */
12917  Roo.tree.AsyncTreeNode = function(config){
12918     this.loaded = false;
12919     this.loading = false;
12920     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12921     /**
12922     * @event beforeload
12923     * Fires before this node is loaded, return false to cancel
12924     * @param {Node} this This node
12925     */
12926     this.addEvents({'beforeload':true, 'load': true});
12927     /**
12928     * @event load
12929     * Fires when this node is loaded
12930     * @param {Node} this This node
12931     */
12932     /**
12933      * The loader used by this node (defaults to using the tree's defined loader)
12934      * @type TreeLoader
12935      * @property loader
12936      */
12937 };
12938 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12939     expand : function(deep, anim, callback){
12940         if(this.loading){ // if an async load is already running, waiting til it's done
12941             var timer;
12942             var f = function(){
12943                 if(!this.loading){ // done loading
12944                     clearInterval(timer);
12945                     this.expand(deep, anim, callback);
12946                 }
12947             }.createDelegate(this);
12948             timer = setInterval(f, 200);
12949             return;
12950         }
12951         if(!this.loaded){
12952             if(this.fireEvent("beforeload", this) === false){
12953                 return;
12954             }
12955             this.loading = true;
12956             this.ui.beforeLoad(this);
12957             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12958             if(loader){
12959                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12960                 return;
12961             }
12962         }
12963         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12964     },
12965     
12966     /**
12967      * Returns true if this node is currently loading
12968      * @return {Boolean}
12969      */
12970     isLoading : function(){
12971         return this.loading;  
12972     },
12973     
12974     loadComplete : function(deep, anim, callback){
12975         this.loading = false;
12976         this.loaded = true;
12977         this.ui.afterLoad(this);
12978         this.fireEvent("load", this);
12979         this.expand(deep, anim, callback);
12980     },
12981     
12982     /**
12983      * Returns true if this node has been loaded
12984      * @return {Boolean}
12985      */
12986     isLoaded : function(){
12987         return this.loaded;
12988     },
12989     
12990     hasChildNodes : function(){
12991         if(!this.isLeaf() && !this.loaded){
12992             return true;
12993         }else{
12994             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12995         }
12996     },
12997
12998     /**
12999      * Trigger a reload for this node
13000      * @param {Function} callback
13001      */
13002     reload : function(callback){
13003         this.collapse(false, false);
13004         while(this.firstChild){
13005             this.removeChild(this.firstChild);
13006         }
13007         this.childrenRendered = false;
13008         this.loaded = false;
13009         if(this.isHiddenRoot()){
13010             this.expanded = false;
13011         }
13012         this.expand(false, false, callback);
13013     }
13014 });/*
13015  * Based on:
13016  * Ext JS Library 1.1.1
13017  * Copyright(c) 2006-2007, Ext JS, LLC.
13018  *
13019  * Originally Released Under LGPL - original licence link has changed is not relivant.
13020  *
13021  * Fork - LGPL
13022  * <script type="text/javascript">
13023  */
13024  
13025 /**
13026  * @class Roo.tree.TreeNodeUI
13027  * @constructor
13028  * @param {Object} node The node to render
13029  * The TreeNode UI implementation is separate from the
13030  * tree implementation. Unless you are customizing the tree UI,
13031  * you should never have to use this directly.
13032  */
13033 Roo.tree.TreeNodeUI = function(node){
13034     this.node = node;
13035     this.rendered = false;
13036     this.animating = false;
13037     this.emptyIcon = Roo.BLANK_IMAGE_URL;
13038 };
13039
13040 Roo.tree.TreeNodeUI.prototype = {
13041     removeChild : function(node){
13042         if(this.rendered){
13043             this.ctNode.removeChild(node.ui.getEl());
13044         }
13045     },
13046
13047     beforeLoad : function(){
13048          this.addClass("x-tree-node-loading");
13049     },
13050
13051     afterLoad : function(){
13052          this.removeClass("x-tree-node-loading");
13053     },
13054
13055     onTextChange : function(node, text, oldText){
13056         if(this.rendered){
13057             this.textNode.innerHTML = text;
13058         }
13059     },
13060
13061     onDisableChange : function(node, state){
13062         this.disabled = state;
13063         if(state){
13064             this.addClass("x-tree-node-disabled");
13065         }else{
13066             this.removeClass("x-tree-node-disabled");
13067         }
13068     },
13069
13070     onSelectedChange : function(state){
13071         if(state){
13072             this.focus();
13073             this.addClass("x-tree-selected");
13074         }else{
13075             //this.blur();
13076             this.removeClass("x-tree-selected");
13077         }
13078     },
13079
13080     onMove : function(tree, node, oldParent, newParent, index, refNode){
13081         this.childIndent = null;
13082         if(this.rendered){
13083             var targetNode = newParent.ui.getContainer();
13084             if(!targetNode){//target not rendered
13085                 this.holder = document.createElement("div");
13086                 this.holder.appendChild(this.wrap);
13087                 return;
13088             }
13089             var insertBefore = refNode ? refNode.ui.getEl() : null;
13090             if(insertBefore){
13091                 targetNode.insertBefore(this.wrap, insertBefore);
13092             }else{
13093                 targetNode.appendChild(this.wrap);
13094             }
13095             this.node.renderIndent(true);
13096         }
13097     },
13098
13099     addClass : function(cls){
13100         if(this.elNode){
13101             Roo.fly(this.elNode).addClass(cls);
13102         }
13103     },
13104
13105     removeClass : function(cls){
13106         if(this.elNode){
13107             Roo.fly(this.elNode).removeClass(cls);
13108         }
13109     },
13110
13111     remove : function(){
13112         if(this.rendered){
13113             this.holder = document.createElement("div");
13114             this.holder.appendChild(this.wrap);
13115         }
13116     },
13117
13118     fireEvent : function(){
13119         return this.node.fireEvent.apply(this.node, arguments);
13120     },
13121
13122     initEvents : function(){
13123         this.node.on("move", this.onMove, this);
13124         var E = Roo.EventManager;
13125         var a = this.anchor;
13126
13127         var el = Roo.fly(a, '_treeui');
13128
13129         if(Roo.isOpera){ // opera render bug ignores the CSS
13130             el.setStyle("text-decoration", "none");
13131         }
13132
13133         el.on("click", this.onClick, this);
13134         el.on("dblclick", this.onDblClick, this);
13135
13136         if(this.checkbox){
13137             Roo.EventManager.on(this.checkbox,
13138                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13139         }
13140
13141         el.on("contextmenu", this.onContextMenu, this);
13142
13143         var icon = Roo.fly(this.iconNode);
13144         icon.on("click", this.onClick, this);
13145         icon.on("dblclick", this.onDblClick, this);
13146         icon.on("contextmenu", this.onContextMenu, this);
13147         E.on(this.ecNode, "click", this.ecClick, this, true);
13148
13149         if(this.node.disabled){
13150             this.addClass("x-tree-node-disabled");
13151         }
13152         if(this.node.hidden){
13153             this.addClass("x-tree-node-disabled");
13154         }
13155         var ot = this.node.getOwnerTree();
13156         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
13157         if(dd && (!this.node.isRoot || ot.rootVisible)){
13158             Roo.dd.Registry.register(this.elNode, {
13159                 node: this.node,
13160                 handles: this.getDDHandles(),
13161                 isHandle: false
13162             });
13163         }
13164     },
13165
13166     getDDHandles : function(){
13167         return [this.iconNode, this.textNode];
13168     },
13169
13170     hide : function(){
13171         if(this.rendered){
13172             this.wrap.style.display = "none";
13173         }
13174     },
13175
13176     show : function(){
13177         if(this.rendered){
13178             this.wrap.style.display = "";
13179         }
13180     },
13181
13182     onContextMenu : function(e){
13183         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13184             e.preventDefault();
13185             this.focus();
13186             this.fireEvent("contextmenu", this.node, e);
13187         }
13188     },
13189
13190     onClick : function(e){
13191         if(this.dropping){
13192             e.stopEvent();
13193             return;
13194         }
13195         if(this.fireEvent("beforeclick", this.node, e) !== false){
13196             if(!this.disabled && this.node.attributes.href){
13197                 this.fireEvent("click", this.node, e);
13198                 return;
13199             }
13200             e.preventDefault();
13201             if(this.disabled){
13202                 return;
13203             }
13204
13205             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13206                 this.node.toggle();
13207             }
13208
13209             this.fireEvent("click", this.node, e);
13210         }else{
13211             e.stopEvent();
13212         }
13213     },
13214
13215     onDblClick : function(e){
13216         e.preventDefault();
13217         if(this.disabled){
13218             return;
13219         }
13220         if(this.checkbox){
13221             this.toggleCheck();
13222         }
13223         if(!this.animating && this.node.hasChildNodes()){
13224             this.node.toggle();
13225         }
13226         this.fireEvent("dblclick", this.node, e);
13227     },
13228
13229     onCheckChange : function(){
13230         var checked = this.checkbox.checked;
13231         this.node.attributes.checked = checked;
13232         this.fireEvent('checkchange', this.node, checked);
13233     },
13234
13235     ecClick : function(e){
13236         if(!this.animating && this.node.hasChildNodes()){
13237             this.node.toggle();
13238         }
13239     },
13240
13241     startDrop : function(){
13242         this.dropping = true;
13243     },
13244
13245     // delayed drop so the click event doesn't get fired on a drop
13246     endDrop : function(){
13247        setTimeout(function(){
13248            this.dropping = false;
13249        }.createDelegate(this), 50);
13250     },
13251
13252     expand : function(){
13253         this.updateExpandIcon();
13254         this.ctNode.style.display = "";
13255     },
13256
13257     focus : function(){
13258         if(!this.node.preventHScroll){
13259             try{this.anchor.focus();
13260             }catch(e){}
13261         }else if(!Roo.isIE){
13262             try{
13263                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13264                 var l = noscroll.scrollLeft;
13265                 this.anchor.focus();
13266                 noscroll.scrollLeft = l;
13267             }catch(e){}
13268         }
13269     },
13270
13271     toggleCheck : function(value){
13272         var cb = this.checkbox;
13273         if(cb){
13274             cb.checked = (value === undefined ? !cb.checked : value);
13275         }
13276     },
13277
13278     blur : function(){
13279         try{
13280             this.anchor.blur();
13281         }catch(e){}
13282     },
13283
13284     animExpand : function(callback){
13285         var ct = Roo.get(this.ctNode);
13286         ct.stopFx();
13287         if(!this.node.hasChildNodes()){
13288             this.updateExpandIcon();
13289             this.ctNode.style.display = "";
13290             Roo.callback(callback);
13291             return;
13292         }
13293         this.animating = true;
13294         this.updateExpandIcon();
13295
13296         ct.slideIn('t', {
13297            callback : function(){
13298                this.animating = false;
13299                Roo.callback(callback);
13300             },
13301             scope: this,
13302             duration: this.node.ownerTree.duration || .25
13303         });
13304     },
13305
13306     highlight : function(){
13307         var tree = this.node.getOwnerTree();
13308         Roo.fly(this.wrap).highlight(
13309             tree.hlColor || "C3DAF9",
13310             {endColor: tree.hlBaseColor}
13311         );
13312     },
13313
13314     collapse : function(){
13315         this.updateExpandIcon();
13316         this.ctNode.style.display = "none";
13317     },
13318
13319     animCollapse : function(callback){
13320         var ct = Roo.get(this.ctNode);
13321         ct.enableDisplayMode('block');
13322         ct.stopFx();
13323
13324         this.animating = true;
13325         this.updateExpandIcon();
13326
13327         ct.slideOut('t', {
13328             callback : function(){
13329                this.animating = false;
13330                Roo.callback(callback);
13331             },
13332             scope: this,
13333             duration: this.node.ownerTree.duration || .25
13334         });
13335     },
13336
13337     getContainer : function(){
13338         return this.ctNode;
13339     },
13340
13341     getEl : function(){
13342         return this.wrap;
13343     },
13344
13345     appendDDGhost : function(ghostNode){
13346         ghostNode.appendChild(this.elNode.cloneNode(true));
13347     },
13348
13349     getDDRepairXY : function(){
13350         return Roo.lib.Dom.getXY(this.iconNode);
13351     },
13352
13353     onRender : function(){
13354         this.render();
13355     },
13356
13357     render : function(bulkRender){
13358         var n = this.node, a = n.attributes;
13359         var targetNode = n.parentNode ?
13360               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13361
13362         if(!this.rendered){
13363             this.rendered = true;
13364
13365             this.renderElements(n, a, targetNode, bulkRender);
13366
13367             if(a.qtip){
13368                if(this.textNode.setAttributeNS){
13369                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13370                    if(a.qtipTitle){
13371                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13372                    }
13373                }else{
13374                    this.textNode.setAttribute("ext:qtip", a.qtip);
13375                    if(a.qtipTitle){
13376                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13377                    }
13378                }
13379             }else if(a.qtipCfg){
13380                 a.qtipCfg.target = Roo.id(this.textNode);
13381                 Roo.QuickTips.register(a.qtipCfg);
13382             }
13383             this.initEvents();
13384             if(!this.node.expanded){
13385                 this.updateExpandIcon();
13386             }
13387         }else{
13388             if(bulkRender === true) {
13389                 targetNode.appendChild(this.wrap);
13390             }
13391         }
13392     },
13393
13394     renderElements : function(n, a, targetNode, bulkRender)
13395     {
13396         // add some indent caching, this helps performance when rendering a large tree
13397         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13398         var t = n.getOwnerTree();
13399         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13400         if (typeof(n.attributes.html) != 'undefined') {
13401             txt = n.attributes.html;
13402         }
13403         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13404         var cb = typeof a.checked == 'boolean';
13405         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13406         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13407             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13408             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13409             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13410             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13411             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13412              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13413                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13414             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13415             "</li>"];
13416
13417         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13418             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13419                                 n.nextSibling.ui.getEl(), buf.join(""));
13420         }else{
13421             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13422         }
13423
13424         this.elNode = this.wrap.childNodes[0];
13425         this.ctNode = this.wrap.childNodes[1];
13426         var cs = this.elNode.childNodes;
13427         this.indentNode = cs[0];
13428         this.ecNode = cs[1];
13429         this.iconNode = cs[2];
13430         var index = 3;
13431         if(cb){
13432             this.checkbox = cs[3];
13433             index++;
13434         }
13435         this.anchor = cs[index];
13436         this.textNode = cs[index].firstChild;
13437     },
13438
13439     getAnchor : function(){
13440         return this.anchor;
13441     },
13442
13443     getTextEl : function(){
13444         return this.textNode;
13445     },
13446
13447     getIconEl : function(){
13448         return this.iconNode;
13449     },
13450
13451     isChecked : function(){
13452         return this.checkbox ? this.checkbox.checked : false;
13453     },
13454
13455     updateExpandIcon : function(){
13456         if(this.rendered){
13457             var n = this.node, c1, c2;
13458             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13459             var hasChild = n.hasChildNodes();
13460             if(hasChild){
13461                 if(n.expanded){
13462                     cls += "-minus";
13463                     c1 = "x-tree-node-collapsed";
13464                     c2 = "x-tree-node-expanded";
13465                 }else{
13466                     cls += "-plus";
13467                     c1 = "x-tree-node-expanded";
13468                     c2 = "x-tree-node-collapsed";
13469                 }
13470                 if(this.wasLeaf){
13471                     this.removeClass("x-tree-node-leaf");
13472                     this.wasLeaf = false;
13473                 }
13474                 if(this.c1 != c1 || this.c2 != c2){
13475                     Roo.fly(this.elNode).replaceClass(c1, c2);
13476                     this.c1 = c1; this.c2 = c2;
13477                 }
13478             }else{
13479                 // this changes non-leafs into leafs if they have no children.
13480                 // it's not very rational behaviour..
13481                 
13482                 if(!this.wasLeaf && this.node.leaf){
13483                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13484                     delete this.c1;
13485                     delete this.c2;
13486                     this.wasLeaf = true;
13487                 }
13488             }
13489             var ecc = "x-tree-ec-icon "+cls;
13490             if(this.ecc != ecc){
13491                 this.ecNode.className = ecc;
13492                 this.ecc = ecc;
13493             }
13494         }
13495     },
13496
13497     getChildIndent : function(){
13498         if(!this.childIndent){
13499             var buf = [];
13500             var p = this.node;
13501             while(p){
13502                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13503                     if(!p.isLast()) {
13504                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13505                     } else {
13506                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13507                     }
13508                 }
13509                 p = p.parentNode;
13510             }
13511             this.childIndent = buf.join("");
13512         }
13513         return this.childIndent;
13514     },
13515
13516     renderIndent : function(){
13517         if(this.rendered){
13518             var indent = "";
13519             var p = this.node.parentNode;
13520             if(p){
13521                 indent = p.ui.getChildIndent();
13522             }
13523             if(this.indentMarkup != indent){ // don't rerender if not required
13524                 this.indentNode.innerHTML = indent;
13525                 this.indentMarkup = indent;
13526             }
13527             this.updateExpandIcon();
13528         }
13529     }
13530 };
13531
13532 Roo.tree.RootTreeNodeUI = function(){
13533     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13534 };
13535 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13536     render : function(){
13537         if(!this.rendered){
13538             var targetNode = this.node.ownerTree.innerCt.dom;
13539             this.node.expanded = true;
13540             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13541             this.wrap = this.ctNode = targetNode.firstChild;
13542         }
13543     },
13544     collapse : function(){
13545     },
13546     expand : function(){
13547     }
13548 });/*
13549  * Based on:
13550  * Ext JS Library 1.1.1
13551  * Copyright(c) 2006-2007, Ext JS, LLC.
13552  *
13553  * Originally Released Under LGPL - original licence link has changed is not relivant.
13554  *
13555  * Fork - LGPL
13556  * <script type="text/javascript">
13557  */
13558 /**
13559  * @class Roo.tree.TreeLoader
13560  * @extends Roo.util.Observable
13561  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13562  * nodes from a specified URL. The response must be a javascript Array definition
13563  * who's elements are node definition objects. eg:
13564  * <pre><code>
13565 {  success : true,
13566    data :      [
13567    
13568     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13569     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13570     ]
13571 }
13572
13573
13574 </code></pre>
13575  * <br><br>
13576  * The old style respose with just an array is still supported, but not recommended.
13577  * <br><br>
13578  *
13579  * A server request is sent, and child nodes are loaded only when a node is expanded.
13580  * The loading node's id is passed to the server under the parameter name "node" to
13581  * enable the server to produce the correct child nodes.
13582  * <br><br>
13583  * To pass extra parameters, an event handler may be attached to the "beforeload"
13584  * event, and the parameters specified in the TreeLoader's baseParams property:
13585  * <pre><code>
13586     myTreeLoader.on("beforeload", function(treeLoader, node) {
13587         this.baseParams.category = node.attributes.category;
13588     }, this);
13589     
13590 </code></pre>
13591  *
13592  * This would pass an HTTP parameter called "category" to the server containing
13593  * the value of the Node's "category" attribute.
13594  * @constructor
13595  * Creates a new Treeloader.
13596  * @param {Object} config A config object containing config properties.
13597  */
13598 Roo.tree.TreeLoader = function(config){
13599     this.baseParams = {};
13600     this.requestMethod = "POST";
13601     Roo.apply(this, config);
13602
13603     this.addEvents({
13604     
13605         /**
13606          * @event beforeload
13607          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13608          * @param {Object} This TreeLoader object.
13609          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13610          * @param {Object} callback The callback function specified in the {@link #load} call.
13611          */
13612         beforeload : true,
13613         /**
13614          * @event load
13615          * Fires when the node has been successfuly loaded.
13616          * @param {Object} This TreeLoader object.
13617          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13618          * @param {Object} response The response object containing the data from the server.
13619          */
13620         load : true,
13621         /**
13622          * @event loadexception
13623          * Fires if the network request failed.
13624          * @param {Object} This TreeLoader object.
13625          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13626          * @param {Object} response The response object containing the data from the server.
13627          */
13628         loadexception : true,
13629         /**
13630          * @event create
13631          * Fires before a node is created, enabling you to return custom Node types 
13632          * @param {Object} This TreeLoader object.
13633          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13634          */
13635         create : true
13636     });
13637
13638     Roo.tree.TreeLoader.superclass.constructor.call(this);
13639 };
13640
13641 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13642     /**
13643     * @cfg {String} dataUrl The URL from which to request a Json string which
13644     * specifies an array of node definition object representing the child nodes
13645     * to be loaded.
13646     */
13647     /**
13648     * @cfg {String} requestMethod either GET or POST
13649     * defaults to POST (due to BC)
13650     * to be loaded.
13651     */
13652     /**
13653     * @cfg {Object} baseParams (optional) An object containing properties which
13654     * specify HTTP parameters to be passed to each request for child nodes.
13655     */
13656     /**
13657     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13658     * created by this loader. If the attributes sent by the server have an attribute in this object,
13659     * they take priority.
13660     */
13661     /**
13662     * @cfg {Object} uiProviders (optional) An object containing properties which
13663     * 
13664     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13665     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13666     * <i>uiProvider</i> attribute of a returned child node is a string rather
13667     * than a reference to a TreeNodeUI implementation, this that string value
13668     * is used as a property name in the uiProviders object. You can define the provider named
13669     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13670     */
13671     uiProviders : {},
13672
13673     /**
13674     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13675     * child nodes before loading.
13676     */
13677     clearOnLoad : true,
13678
13679     /**
13680     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13681     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13682     * Grid query { data : [ .....] }
13683     */
13684     
13685     root : false,
13686      /**
13687     * @cfg {String} queryParam (optional) 
13688     * Name of the query as it will be passed on the querystring (defaults to 'node')
13689     * eg. the request will be ?node=[id]
13690     */
13691     
13692     
13693     queryParam: false,
13694     
13695     /**
13696      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13697      * This is called automatically when a node is expanded, but may be used to reload
13698      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13699      * @param {Roo.tree.TreeNode} node
13700      * @param {Function} callback
13701      */
13702     load : function(node, callback){
13703         if(this.clearOnLoad){
13704             while(node.firstChild){
13705                 node.removeChild(node.firstChild);
13706             }
13707         }
13708         if(node.attributes.children){ // preloaded json children
13709             var cs = node.attributes.children;
13710             for(var i = 0, len = cs.length; i < len; i++){
13711                 node.appendChild(this.createNode(cs[i]));
13712             }
13713             if(typeof callback == "function"){
13714                 callback();
13715             }
13716         }else if(this.dataUrl){
13717             this.requestData(node, callback);
13718         }
13719     },
13720
13721     getParams: function(node){
13722         var buf = [], bp = this.baseParams;
13723         for(var key in bp){
13724             if(typeof bp[key] != "function"){
13725                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13726             }
13727         }
13728         var n = this.queryParam === false ? 'node' : this.queryParam;
13729         buf.push(n + "=", encodeURIComponent(node.id));
13730         return buf.join("");
13731     },
13732
13733     requestData : function(node, callback){
13734         if(this.fireEvent("beforeload", this, node, callback) !== false){
13735             this.transId = Roo.Ajax.request({
13736                 method:this.requestMethod,
13737                 url: this.dataUrl||this.url,
13738                 success: this.handleResponse,
13739                 failure: this.handleFailure,
13740                 scope: this,
13741                 argument: {callback: callback, node: node},
13742                 params: this.getParams(node)
13743             });
13744         }else{
13745             // if the load is cancelled, make sure we notify
13746             // the node that we are done
13747             if(typeof callback == "function"){
13748                 callback();
13749             }
13750         }
13751     },
13752
13753     isLoading : function(){
13754         return this.transId ? true : false;
13755     },
13756
13757     abort : function(){
13758         if(this.isLoading()){
13759             Roo.Ajax.abort(this.transId);
13760         }
13761     },
13762
13763     // private
13764     createNode : function(attr)
13765     {
13766         // apply baseAttrs, nice idea Corey!
13767         if(this.baseAttrs){
13768             Roo.applyIf(attr, this.baseAttrs);
13769         }
13770         if(this.applyLoader !== false){
13771             attr.loader = this;
13772         }
13773         // uiProvider = depreciated..
13774         
13775         if(typeof(attr.uiProvider) == 'string'){
13776            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13777                 /**  eval:var:attr */ eval(attr.uiProvider);
13778         }
13779         if(typeof(this.uiProviders['default']) != 'undefined') {
13780             attr.uiProvider = this.uiProviders['default'];
13781         }
13782         
13783         this.fireEvent('create', this, attr);
13784         
13785         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13786         return(attr.leaf ?
13787                         new Roo.tree.TreeNode(attr) :
13788                         new Roo.tree.AsyncTreeNode(attr));
13789     },
13790
13791     processResponse : function(response, node, callback)
13792     {
13793         var json = response.responseText;
13794         try {
13795             
13796             var o = Roo.decode(json);
13797             
13798             if (this.root === false && typeof(o.success) != undefined) {
13799                 this.root = 'data'; // the default behaviour for list like data..
13800                 }
13801                 
13802             if (this.root !== false &&  !o.success) {
13803                 // it's a failure condition.
13804                 var a = response.argument;
13805                 this.fireEvent("loadexception", this, a.node, response);
13806                 Roo.log("Load failed - should have a handler really");
13807                 return;
13808             }
13809             
13810             
13811             
13812             if (this.root !== false) {
13813                  o = o[this.root];
13814             }
13815             
13816             for(var i = 0, len = o.length; i < len; i++){
13817                 var n = this.createNode(o[i]);
13818                 if(n){
13819                     node.appendChild(n);
13820                 }
13821             }
13822             if(typeof callback == "function"){
13823                 callback(this, node);
13824             }
13825         }catch(e){
13826             this.handleFailure(response);
13827         }
13828     },
13829
13830     handleResponse : function(response){
13831         this.transId = false;
13832         var a = response.argument;
13833         this.processResponse(response, a.node, a.callback);
13834         this.fireEvent("load", this, a.node, response);
13835     },
13836
13837     handleFailure : function(response)
13838     {
13839         // should handle failure better..
13840         this.transId = false;
13841         var a = response.argument;
13842         this.fireEvent("loadexception", this, a.node, response);
13843         if(typeof a.callback == "function"){
13844             a.callback(this, a.node);
13845         }
13846     }
13847 });/*
13848  * Based on:
13849  * Ext JS Library 1.1.1
13850  * Copyright(c) 2006-2007, Ext JS, LLC.
13851  *
13852  * Originally Released Under LGPL - original licence link has changed is not relivant.
13853  *
13854  * Fork - LGPL
13855  * <script type="text/javascript">
13856  */
13857
13858 /**
13859 * @class Roo.tree.TreeFilter
13860 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13861 * @param {TreePanel} tree
13862 * @param {Object} config (optional)
13863  */
13864 Roo.tree.TreeFilter = function(tree, config){
13865     this.tree = tree;
13866     this.filtered = {};
13867     Roo.apply(this, config);
13868 };
13869
13870 Roo.tree.TreeFilter.prototype = {
13871     clearBlank:false,
13872     reverse:false,
13873     autoClear:false,
13874     remove:false,
13875
13876      /**
13877      * Filter the data by a specific attribute.
13878      * @param {String/RegExp} value Either string that the attribute value
13879      * should start with or a RegExp to test against the attribute
13880      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13881      * @param {TreeNode} startNode (optional) The node to start the filter at.
13882      */
13883     filter : function(value, attr, startNode){
13884         attr = attr || "text";
13885         var f;
13886         if(typeof value == "string"){
13887             var vlen = value.length;
13888             // auto clear empty filter
13889             if(vlen == 0 && this.clearBlank){
13890                 this.clear();
13891                 return;
13892             }
13893             value = value.toLowerCase();
13894             f = function(n){
13895                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13896             };
13897         }else if(value.exec){ // regex?
13898             f = function(n){
13899                 return value.test(n.attributes[attr]);
13900             };
13901         }else{
13902             throw 'Illegal filter type, must be string or regex';
13903         }
13904         this.filterBy(f, null, startNode);
13905         },
13906
13907     /**
13908      * Filter by a function. The passed function will be called with each
13909      * node in the tree (or from the startNode). If the function returns true, the node is kept
13910      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13911      * @param {Function} fn The filter function
13912      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13913      */
13914     filterBy : function(fn, scope, startNode){
13915         startNode = startNode || this.tree.root;
13916         if(this.autoClear){
13917             this.clear();
13918         }
13919         var af = this.filtered, rv = this.reverse;
13920         var f = function(n){
13921             if(n == startNode){
13922                 return true;
13923             }
13924             if(af[n.id]){
13925                 return false;
13926             }
13927             var m = fn.call(scope || n, n);
13928             if(!m || rv){
13929                 af[n.id] = n;
13930                 n.ui.hide();
13931                 return false;
13932             }
13933             return true;
13934         };
13935         startNode.cascade(f);
13936         if(this.remove){
13937            for(var id in af){
13938                if(typeof id != "function"){
13939                    var n = af[id];
13940                    if(n && n.parentNode){
13941                        n.parentNode.removeChild(n);
13942                    }
13943                }
13944            }
13945         }
13946     },
13947
13948     /**
13949      * Clears the current filter. Note: with the "remove" option
13950      * set a filter cannot be cleared.
13951      */
13952     clear : function(){
13953         var t = this.tree;
13954         var af = this.filtered;
13955         for(var id in af){
13956             if(typeof id != "function"){
13957                 var n = af[id];
13958                 if(n){
13959                     n.ui.show();
13960                 }
13961             }
13962         }
13963         this.filtered = {};
13964     }
13965 };
13966 /*
13967  * Based on:
13968  * Ext JS Library 1.1.1
13969  * Copyright(c) 2006-2007, Ext JS, LLC.
13970  *
13971  * Originally Released Under LGPL - original licence link has changed is not relivant.
13972  *
13973  * Fork - LGPL
13974  * <script type="text/javascript">
13975  */
13976  
13977
13978 /**
13979  * @class Roo.tree.TreeSorter
13980  * Provides sorting of nodes in a TreePanel
13981  * 
13982  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13983  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13984  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13985  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13986  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13987  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13988  * @constructor
13989  * @param {TreePanel} tree
13990  * @param {Object} config
13991  */
13992 Roo.tree.TreeSorter = function(tree, config){
13993     Roo.apply(this, config);
13994     tree.on("beforechildrenrendered", this.doSort, this);
13995     tree.on("append", this.updateSort, this);
13996     tree.on("insert", this.updateSort, this);
13997     
13998     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13999     var p = this.property || "text";
14000     var sortType = this.sortType;
14001     var fs = this.folderSort;
14002     var cs = this.caseSensitive === true;
14003     var leafAttr = this.leafAttr || 'leaf';
14004
14005     this.sortFn = function(n1, n2){
14006         if(fs){
14007             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
14008                 return 1;
14009             }
14010             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
14011                 return -1;
14012             }
14013         }
14014         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
14015         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
14016         if(v1 < v2){
14017                         return dsc ? +1 : -1;
14018                 }else if(v1 > v2){
14019                         return dsc ? -1 : +1;
14020         }else{
14021                 return 0;
14022         }
14023     };
14024 };
14025
14026 Roo.tree.TreeSorter.prototype = {
14027     doSort : function(node){
14028         node.sort(this.sortFn);
14029     },
14030     
14031     compareNodes : function(n1, n2){
14032         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
14033     },
14034     
14035     updateSort : function(tree, node){
14036         if(node.childrenRendered){
14037             this.doSort.defer(1, this, [node]);
14038         }
14039     }
14040 };/*
14041  * Based on:
14042  * Ext JS Library 1.1.1
14043  * Copyright(c) 2006-2007, Ext JS, LLC.
14044  *
14045  * Originally Released Under LGPL - original licence link has changed is not relivant.
14046  *
14047  * Fork - LGPL
14048  * <script type="text/javascript">
14049  */
14050
14051 if(Roo.dd.DropZone){
14052     
14053 Roo.tree.TreeDropZone = function(tree, config){
14054     this.allowParentInsert = false;
14055     this.allowContainerDrop = false;
14056     this.appendOnly = false;
14057     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14058     this.tree = tree;
14059     this.lastInsertClass = "x-tree-no-status";
14060     this.dragOverData = {};
14061 };
14062
14063 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14064     ddGroup : "TreeDD",
14065     scroll:  true,
14066     
14067     expandDelay : 1000,
14068     
14069     expandNode : function(node){
14070         if(node.hasChildNodes() && !node.isExpanded()){
14071             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14072         }
14073     },
14074     
14075     queueExpand : function(node){
14076         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14077     },
14078     
14079     cancelExpand : function(){
14080         if(this.expandProcId){
14081             clearTimeout(this.expandProcId);
14082             this.expandProcId = false;
14083         }
14084     },
14085     
14086     isValidDropPoint : function(n, pt, dd, e, data){
14087         if(!n || !data){ return false; }
14088         var targetNode = n.node;
14089         var dropNode = data.node;
14090         // default drop rules
14091         if(!(targetNode && targetNode.isTarget && pt)){
14092             return false;
14093         }
14094         if(pt == "append" && targetNode.allowChildren === false){
14095             return false;
14096         }
14097         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14098             return false;
14099         }
14100         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14101             return false;
14102         }
14103         // reuse the object
14104         var overEvent = this.dragOverData;
14105         overEvent.tree = this.tree;
14106         overEvent.target = targetNode;
14107         overEvent.data = data;
14108         overEvent.point = pt;
14109         overEvent.source = dd;
14110         overEvent.rawEvent = e;
14111         overEvent.dropNode = dropNode;
14112         overEvent.cancel = false;  
14113         var result = this.tree.fireEvent("nodedragover", overEvent);
14114         return overEvent.cancel === false && result !== false;
14115     },
14116     
14117     getDropPoint : function(e, n, dd)
14118     {
14119         var tn = n.node;
14120         if(tn.isRoot){
14121             return tn.allowChildren !== false ? "append" : false; // always append for root
14122         }
14123         var dragEl = n.ddel;
14124         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14125         var y = Roo.lib.Event.getPageY(e);
14126         //var noAppend = tn.allowChildren === false || tn.isLeaf();
14127         
14128         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14129         var noAppend = tn.allowChildren === false;
14130         if(this.appendOnly || tn.parentNode.allowChildren === false){
14131             return noAppend ? false : "append";
14132         }
14133         var noBelow = false;
14134         if(!this.allowParentInsert){
14135             noBelow = tn.hasChildNodes() && tn.isExpanded();
14136         }
14137         var q = (b - t) / (noAppend ? 2 : 3);
14138         if(y >= t && y < (t + q)){
14139             return "above";
14140         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14141             return "below";
14142         }else{
14143             return "append";
14144         }
14145     },
14146     
14147     onNodeEnter : function(n, dd, e, data)
14148     {
14149         this.cancelExpand();
14150     },
14151     
14152     onNodeOver : function(n, dd, e, data)
14153     {
14154        
14155         var pt = this.getDropPoint(e, n, dd);
14156         var node = n.node;
14157         
14158         // auto node expand check
14159         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14160             this.queueExpand(node);
14161         }else if(pt != "append"){
14162             this.cancelExpand();
14163         }
14164         
14165         // set the insert point style on the target node
14166         var returnCls = this.dropNotAllowed;
14167         if(this.isValidDropPoint(n, pt, dd, e, data)){
14168            if(pt){
14169                var el = n.ddel;
14170                var cls;
14171                if(pt == "above"){
14172                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14173                    cls = "x-tree-drag-insert-above";
14174                }else if(pt == "below"){
14175                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14176                    cls = "x-tree-drag-insert-below";
14177                }else{
14178                    returnCls = "x-tree-drop-ok-append";
14179                    cls = "x-tree-drag-append";
14180                }
14181                if(this.lastInsertClass != cls){
14182                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14183                    this.lastInsertClass = cls;
14184                }
14185            }
14186        }
14187        return returnCls;
14188     },
14189     
14190     onNodeOut : function(n, dd, e, data){
14191         
14192         this.cancelExpand();
14193         this.removeDropIndicators(n);
14194     },
14195     
14196     onNodeDrop : function(n, dd, e, data){
14197         var point = this.getDropPoint(e, n, dd);
14198         var targetNode = n.node;
14199         targetNode.ui.startDrop();
14200         if(!this.isValidDropPoint(n, point, dd, e, data)){
14201             targetNode.ui.endDrop();
14202             return false;
14203         }
14204         // first try to find the drop node
14205         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14206         var dropEvent = {
14207             tree : this.tree,
14208             target: targetNode,
14209             data: data,
14210             point: point,
14211             source: dd,
14212             rawEvent: e,
14213             dropNode: dropNode,
14214             cancel: !dropNode   
14215         };
14216         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14217         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14218             targetNode.ui.endDrop();
14219             return false;
14220         }
14221         // allow target changing
14222         targetNode = dropEvent.target;
14223         if(point == "append" && !targetNode.isExpanded()){
14224             targetNode.expand(false, null, function(){
14225                 this.completeDrop(dropEvent);
14226             }.createDelegate(this));
14227         }else{
14228             this.completeDrop(dropEvent);
14229         }
14230         return true;
14231     },
14232     
14233     completeDrop : function(de){
14234         var ns = de.dropNode, p = de.point, t = de.target;
14235         if(!(ns instanceof Array)){
14236             ns = [ns];
14237         }
14238         var n;
14239         for(var i = 0, len = ns.length; i < len; i++){
14240             n = ns[i];
14241             if(p == "above"){
14242                 t.parentNode.insertBefore(n, t);
14243             }else if(p == "below"){
14244                 t.parentNode.insertBefore(n, t.nextSibling);
14245             }else{
14246                 t.appendChild(n);
14247             }
14248         }
14249         n.ui.focus();
14250         if(this.tree.hlDrop){
14251             n.ui.highlight();
14252         }
14253         t.ui.endDrop();
14254         this.tree.fireEvent("nodedrop", de);
14255     },
14256     
14257     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14258         if(this.tree.hlDrop){
14259             dropNode.ui.focus();
14260             dropNode.ui.highlight();
14261         }
14262         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14263     },
14264     
14265     getTree : function(){
14266         return this.tree;
14267     },
14268     
14269     removeDropIndicators : function(n){
14270         if(n && n.ddel){
14271             var el = n.ddel;
14272             Roo.fly(el).removeClass([
14273                     "x-tree-drag-insert-above",
14274                     "x-tree-drag-insert-below",
14275                     "x-tree-drag-append"]);
14276             this.lastInsertClass = "_noclass";
14277         }
14278     },
14279     
14280     beforeDragDrop : function(target, e, id){
14281         this.cancelExpand();
14282         return true;
14283     },
14284     
14285     afterRepair : function(data){
14286         if(data && Roo.enableFx){
14287             data.node.ui.highlight();
14288         }
14289         this.hideProxy();
14290     } 
14291     
14292 });
14293
14294 }
14295 /*
14296  * Based on:
14297  * Ext JS Library 1.1.1
14298  * Copyright(c) 2006-2007, Ext JS, LLC.
14299  *
14300  * Originally Released Under LGPL - original licence link has changed is not relivant.
14301  *
14302  * Fork - LGPL
14303  * <script type="text/javascript">
14304  */
14305  
14306
14307 if(Roo.dd.DragZone){
14308 Roo.tree.TreeDragZone = function(tree, config){
14309     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14310     this.tree = tree;
14311 };
14312
14313 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14314     ddGroup : "TreeDD",
14315    
14316     onBeforeDrag : function(data, e){
14317         var n = data.node;
14318         return n && n.draggable && !n.disabled;
14319     },
14320      
14321     
14322     onInitDrag : function(e){
14323         var data = this.dragData;
14324         this.tree.getSelectionModel().select(data.node);
14325         this.proxy.update("");
14326         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14327         this.tree.fireEvent("startdrag", this.tree, data.node, e);
14328     },
14329     
14330     getRepairXY : function(e, data){
14331         return data.node.ui.getDDRepairXY();
14332     },
14333     
14334     onEndDrag : function(data, e){
14335         this.tree.fireEvent("enddrag", this.tree, data.node, e);
14336         
14337         
14338     },
14339     
14340     onValidDrop : function(dd, e, id){
14341         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14342         this.hideProxy();
14343     },
14344     
14345     beforeInvalidDrop : function(e, id){
14346         // this scrolls the original position back into view
14347         var sm = this.tree.getSelectionModel();
14348         sm.clearSelections();
14349         sm.select(this.dragData.node);
14350     }
14351 });
14352 }/*
14353  * Based on:
14354  * Ext JS Library 1.1.1
14355  * Copyright(c) 2006-2007, Ext JS, LLC.
14356  *
14357  * Originally Released Under LGPL - original licence link has changed is not relivant.
14358  *
14359  * Fork - LGPL
14360  * <script type="text/javascript">
14361  */
14362 /**
14363  * @class Roo.tree.TreeEditor
14364  * @extends Roo.Editor
14365  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14366  * as the editor field.
14367  * @constructor
14368  * @param {Object} config (used to be the tree panel.)
14369  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14370  * 
14371  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14372  * @cfg {Roo.form.TextField|Object} field The field configuration
14373  *
14374  * 
14375  */
14376 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14377     var tree = config;
14378     var field;
14379     if (oldconfig) { // old style..
14380         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14381     } else {
14382         // new style..
14383         tree = config.tree;
14384         config.field = config.field  || {};
14385         config.field.xtype = 'TextField';
14386         field = Roo.factory(config.field, Roo.form);
14387     }
14388     config = config || {};
14389     
14390     
14391     this.addEvents({
14392         /**
14393          * @event beforenodeedit
14394          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14395          * false from the handler of this event.
14396          * @param {Editor} this
14397          * @param {Roo.tree.Node} node 
14398          */
14399         "beforenodeedit" : true
14400     });
14401     
14402     //Roo.log(config);
14403     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14404
14405     this.tree = tree;
14406
14407     tree.on('beforeclick', this.beforeNodeClick, this);
14408     tree.getTreeEl().on('mousedown', this.hide, this);
14409     this.on('complete', this.updateNode, this);
14410     this.on('beforestartedit', this.fitToTree, this);
14411     this.on('startedit', this.bindScroll, this, {delay:10});
14412     this.on('specialkey', this.onSpecialKey, this);
14413 };
14414
14415 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14416     /**
14417      * @cfg {String} alignment
14418      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14419      */
14420     alignment: "l-l",
14421     // inherit
14422     autoSize: false,
14423     /**
14424      * @cfg {Boolean} hideEl
14425      * True to hide the bound element while the editor is displayed (defaults to false)
14426      */
14427     hideEl : false,
14428     /**
14429      * @cfg {String} cls
14430      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14431      */
14432     cls: "x-small-editor x-tree-editor",
14433     /**
14434      * @cfg {Boolean} shim
14435      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14436      */
14437     shim:false,
14438     // inherit
14439     shadow:"frame",
14440     /**
14441      * @cfg {Number} maxWidth
14442      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14443      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14444      * scroll and client offsets into account prior to each edit.
14445      */
14446     maxWidth: 250,
14447
14448     editDelay : 350,
14449
14450     // private
14451     fitToTree : function(ed, el){
14452         var td = this.tree.getTreeEl().dom, nd = el.dom;
14453         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14454             td.scrollLeft = nd.offsetLeft;
14455         }
14456         var w = Math.min(
14457                 this.maxWidth,
14458                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14459         this.setSize(w, '');
14460         
14461         return this.fireEvent('beforenodeedit', this, this.editNode);
14462         
14463     },
14464
14465     // private
14466     triggerEdit : function(node){
14467         this.completeEdit();
14468         this.editNode = node;
14469         this.startEdit(node.ui.textNode, node.text);
14470     },
14471
14472     // private
14473     bindScroll : function(){
14474         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14475     },
14476
14477     // private
14478     beforeNodeClick : function(node, e){
14479         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14480         this.lastClick = new Date();
14481         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14482             e.stopEvent();
14483             this.triggerEdit(node);
14484             return false;
14485         }
14486         return true;
14487     },
14488
14489     // private
14490     updateNode : function(ed, value){
14491         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14492         this.editNode.setText(value);
14493     },
14494
14495     // private
14496     onHide : function(){
14497         Roo.tree.TreeEditor.superclass.onHide.call(this);
14498         if(this.editNode){
14499             this.editNode.ui.focus();
14500         }
14501     },
14502
14503     // private
14504     onSpecialKey : function(field, e){
14505         var k = e.getKey();
14506         if(k == e.ESC){
14507             e.stopEvent();
14508             this.cancelEdit();
14509         }else if(k == e.ENTER && !e.hasModifier()){
14510             e.stopEvent();
14511             this.completeEdit();
14512         }
14513     }
14514 });//<Script type="text/javascript">
14515 /*
14516  * Based on:
14517  * Ext JS Library 1.1.1
14518  * Copyright(c) 2006-2007, Ext JS, LLC.
14519  *
14520  * Originally Released Under LGPL - original licence link has changed is not relivant.
14521  *
14522  * Fork - LGPL
14523  * <script type="text/javascript">
14524  */
14525  
14526 /**
14527  * Not documented??? - probably should be...
14528  */
14529
14530 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14531     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14532     
14533     renderElements : function(n, a, targetNode, bulkRender){
14534         //consel.log("renderElements?");
14535         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14536
14537         var t = n.getOwnerTree();
14538         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14539         
14540         var cols = t.columns;
14541         var bw = t.borderWidth;
14542         var c = cols[0];
14543         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14544          var cb = typeof a.checked == "boolean";
14545         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14546         var colcls = 'x-t-' + tid + '-c0';
14547         var buf = [
14548             '<li class="x-tree-node">',
14549             
14550                 
14551                 '<div class="x-tree-node-el ', a.cls,'">',
14552                     // extran...
14553                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14554                 
14555                 
14556                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14557                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14558                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14559                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14560                            (a.iconCls ? ' '+a.iconCls : ''),
14561                            '" unselectable="on" />',
14562                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14563                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14564                              
14565                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14566                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14567                             '<span unselectable="on" qtip="' + tx + '">',
14568                              tx,
14569                              '</span></a>' ,
14570                     '</div>',
14571                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14572                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14573                  ];
14574         for(var i = 1, len = cols.length; i < len; i++){
14575             c = cols[i];
14576             colcls = 'x-t-' + tid + '-c' +i;
14577             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14578             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14579                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14580                       "</div>");
14581          }
14582          
14583          buf.push(
14584             '</a>',
14585             '<div class="x-clear"></div></div>',
14586             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14587             "</li>");
14588         
14589         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14590             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14591                                 n.nextSibling.ui.getEl(), buf.join(""));
14592         }else{
14593             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14594         }
14595         var el = this.wrap.firstChild;
14596         this.elRow = el;
14597         this.elNode = el.firstChild;
14598         this.ranchor = el.childNodes[1];
14599         this.ctNode = this.wrap.childNodes[1];
14600         var cs = el.firstChild.childNodes;
14601         this.indentNode = cs[0];
14602         this.ecNode = cs[1];
14603         this.iconNode = cs[2];
14604         var index = 3;
14605         if(cb){
14606             this.checkbox = cs[3];
14607             index++;
14608         }
14609         this.anchor = cs[index];
14610         
14611         this.textNode = cs[index].firstChild;
14612         
14613         //el.on("click", this.onClick, this);
14614         //el.on("dblclick", this.onDblClick, this);
14615         
14616         
14617        // console.log(this);
14618     },
14619     initEvents : function(){
14620         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14621         
14622             
14623         var a = this.ranchor;
14624
14625         var el = Roo.get(a);
14626
14627         if(Roo.isOpera){ // opera render bug ignores the CSS
14628             el.setStyle("text-decoration", "none");
14629         }
14630
14631         el.on("click", this.onClick, this);
14632         el.on("dblclick", this.onDblClick, this);
14633         el.on("contextmenu", this.onContextMenu, this);
14634         
14635     },
14636     
14637     /*onSelectedChange : function(state){
14638         if(state){
14639             this.focus();
14640             this.addClass("x-tree-selected");
14641         }else{
14642             //this.blur();
14643             this.removeClass("x-tree-selected");
14644         }
14645     },*/
14646     addClass : function(cls){
14647         if(this.elRow){
14648             Roo.fly(this.elRow).addClass(cls);
14649         }
14650         
14651     },
14652     
14653     
14654     removeClass : function(cls){
14655         if(this.elRow){
14656             Roo.fly(this.elRow).removeClass(cls);
14657         }
14658     }
14659
14660     
14661     
14662 });//<Script type="text/javascript">
14663
14664 /*
14665  * Based on:
14666  * Ext JS Library 1.1.1
14667  * Copyright(c) 2006-2007, Ext JS, LLC.
14668  *
14669  * Originally Released Under LGPL - original licence link has changed is not relivant.
14670  *
14671  * Fork - LGPL
14672  * <script type="text/javascript">
14673  */
14674  
14675
14676 /**
14677  * @class Roo.tree.ColumnTree
14678  * @extends Roo.data.TreePanel
14679  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14680  * @cfg {int} borderWidth  compined right/left border allowance
14681  * @constructor
14682  * @param {String/HTMLElement/Element} el The container element
14683  * @param {Object} config
14684  */
14685 Roo.tree.ColumnTree =  function(el, config)
14686 {
14687    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14688    this.addEvents({
14689         /**
14690         * @event resize
14691         * Fire this event on a container when it resizes
14692         * @param {int} w Width
14693         * @param {int} h Height
14694         */
14695        "resize" : true
14696     });
14697     this.on('resize', this.onResize, this);
14698 };
14699
14700 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14701     //lines:false,
14702     
14703     
14704     borderWidth: Roo.isBorderBox ? 0 : 2, 
14705     headEls : false,
14706     
14707     render : function(){
14708         // add the header.....
14709        
14710         Roo.tree.ColumnTree.superclass.render.apply(this);
14711         
14712         this.el.addClass('x-column-tree');
14713         
14714         this.headers = this.el.createChild(
14715             {cls:'x-tree-headers'},this.innerCt.dom);
14716    
14717         var cols = this.columns, c;
14718         var totalWidth = 0;
14719         this.headEls = [];
14720         var  len = cols.length;
14721         for(var i = 0; i < len; i++){
14722              c = cols[i];
14723              totalWidth += c.width;
14724             this.headEls.push(this.headers.createChild({
14725                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14726                  cn: {
14727                      cls:'x-tree-hd-text',
14728                      html: c.header
14729                  },
14730                  style:'width:'+(c.width-this.borderWidth)+'px;'
14731              }));
14732         }
14733         this.headers.createChild({cls:'x-clear'});
14734         // prevent floats from wrapping when clipped
14735         this.headers.setWidth(totalWidth);
14736         //this.innerCt.setWidth(totalWidth);
14737         this.innerCt.setStyle({ overflow: 'auto' });
14738         this.onResize(this.width, this.height);
14739              
14740         
14741     },
14742     onResize : function(w,h)
14743     {
14744         this.height = h;
14745         this.width = w;
14746         // resize cols..
14747         this.innerCt.setWidth(this.width);
14748         this.innerCt.setHeight(this.height-20);
14749         
14750         // headers...
14751         var cols = this.columns, c;
14752         var totalWidth = 0;
14753         var expEl = false;
14754         var len = cols.length;
14755         for(var i = 0; i < len; i++){
14756             c = cols[i];
14757             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14758                 // it's the expander..
14759                 expEl  = this.headEls[i];
14760                 continue;
14761             }
14762             totalWidth += c.width;
14763             
14764         }
14765         if (expEl) {
14766             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14767         }
14768         this.headers.setWidth(w-20);
14769
14770         
14771         
14772         
14773     }
14774 });
14775 /*
14776  * Based on:
14777  * Ext JS Library 1.1.1
14778  * Copyright(c) 2006-2007, Ext JS, LLC.
14779  *
14780  * Originally Released Under LGPL - original licence link has changed is not relivant.
14781  *
14782  * Fork - LGPL
14783  * <script type="text/javascript">
14784  */
14785  
14786 /**
14787  * @class Roo.menu.Menu
14788  * @extends Roo.util.Observable
14789  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14790  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14791  * @constructor
14792  * Creates a new Menu
14793  * @param {Object} config Configuration options
14794  */
14795 Roo.menu.Menu = function(config){
14796     
14797     Roo.menu.Menu.superclass.constructor.call(this, config);
14798     
14799     this.id = this.id || Roo.id();
14800     this.addEvents({
14801         /**
14802          * @event beforeshow
14803          * Fires before this menu is displayed
14804          * @param {Roo.menu.Menu} this
14805          */
14806         beforeshow : true,
14807         /**
14808          * @event beforehide
14809          * Fires before this menu is hidden
14810          * @param {Roo.menu.Menu} this
14811          */
14812         beforehide : true,
14813         /**
14814          * @event show
14815          * Fires after this menu is displayed
14816          * @param {Roo.menu.Menu} this
14817          */
14818         show : true,
14819         /**
14820          * @event hide
14821          * Fires after this menu is hidden
14822          * @param {Roo.menu.Menu} this
14823          */
14824         hide : true,
14825         /**
14826          * @event click
14827          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14828          * @param {Roo.menu.Menu} this
14829          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14830          * @param {Roo.EventObject} e
14831          */
14832         click : true,
14833         /**
14834          * @event mouseover
14835          * Fires when the mouse is hovering over this menu
14836          * @param {Roo.menu.Menu} this
14837          * @param {Roo.EventObject} e
14838          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14839          */
14840         mouseover : true,
14841         /**
14842          * @event mouseout
14843          * Fires when the mouse exits this menu
14844          * @param {Roo.menu.Menu} this
14845          * @param {Roo.EventObject} e
14846          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14847          */
14848         mouseout : true,
14849         /**
14850          * @event itemclick
14851          * Fires when a menu item contained in this menu is clicked
14852          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14853          * @param {Roo.EventObject} e
14854          */
14855         itemclick: true
14856     });
14857     if (this.registerMenu) {
14858         Roo.menu.MenuMgr.register(this);
14859     }
14860     
14861     var mis = this.items;
14862     this.items = new Roo.util.MixedCollection();
14863     if(mis){
14864         this.add.apply(this, mis);
14865     }
14866 };
14867
14868 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14869     /**
14870      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14871      */
14872     minWidth : 120,
14873     /**
14874      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14875      * for bottom-right shadow (defaults to "sides")
14876      */
14877     shadow : "sides",
14878     /**
14879      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14880      * this menu (defaults to "tl-tr?")
14881      */
14882     subMenuAlign : "tl-tr?",
14883     /**
14884      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14885      * relative to its element of origin (defaults to "tl-bl?")
14886      */
14887     defaultAlign : "tl-bl?",
14888     /**
14889      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14890      */
14891     allowOtherMenus : false,
14892     /**
14893      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14894      */
14895     registerMenu : true,
14896
14897     hidden:true,
14898
14899     // private
14900     render : function(){
14901         if(this.el){
14902             return;
14903         }
14904         var el = this.el = new Roo.Layer({
14905             cls: "x-menu",
14906             shadow:this.shadow,
14907             constrain: false,
14908             parentEl: this.parentEl || document.body,
14909             zindex:15000
14910         });
14911
14912         this.keyNav = new Roo.menu.MenuNav(this);
14913
14914         if(this.plain){
14915             el.addClass("x-menu-plain");
14916         }
14917         if(this.cls){
14918             el.addClass(this.cls);
14919         }
14920         // generic focus element
14921         this.focusEl = el.createChild({
14922             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14923         });
14924         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14925         //disabling touch- as it's causing issues ..
14926         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14927         ul.on('click'   , this.onClick, this);
14928         
14929         
14930         ul.on("mouseover", this.onMouseOver, this);
14931         ul.on("mouseout", this.onMouseOut, this);
14932         this.items.each(function(item){
14933             if (item.hidden) {
14934                 return;
14935             }
14936             
14937             var li = document.createElement("li");
14938             li.className = "x-menu-list-item";
14939             ul.dom.appendChild(li);
14940             item.render(li, this);
14941         }, this);
14942         this.ul = ul;
14943         this.autoWidth();
14944     },
14945
14946     // private
14947     autoWidth : function(){
14948         var el = this.el, ul = this.ul;
14949         if(!el){
14950             return;
14951         }
14952         var w = this.width;
14953         if(w){
14954             el.setWidth(w);
14955         }else if(Roo.isIE){
14956             el.setWidth(this.minWidth);
14957             var t = el.dom.offsetWidth; // force recalc
14958             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14959         }
14960     },
14961
14962     // private
14963     delayAutoWidth : function(){
14964         if(this.rendered){
14965             if(!this.awTask){
14966                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14967             }
14968             this.awTask.delay(20);
14969         }
14970     },
14971
14972     // private
14973     findTargetItem : function(e){
14974         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14975         if(t && t.menuItemId){
14976             return this.items.get(t.menuItemId);
14977         }
14978     },
14979
14980     // private
14981     onClick : function(e){
14982         Roo.log("menu.onClick");
14983         var t = this.findTargetItem(e);
14984         if(!t){
14985             return;
14986         }
14987         Roo.log(e);
14988         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14989             if(t == this.activeItem && t.shouldDeactivate(e)){
14990                 this.activeItem.deactivate();
14991                 delete this.activeItem;
14992                 return;
14993             }
14994             if(t.canActivate){
14995                 this.setActiveItem(t, true);
14996             }
14997             return;
14998             
14999             
15000         }
15001         
15002         t.onClick(e);
15003         this.fireEvent("click", this, t, e);
15004     },
15005
15006     // private
15007     setActiveItem : function(item, autoExpand){
15008         if(item != this.activeItem){
15009             if(this.activeItem){
15010                 this.activeItem.deactivate();
15011             }
15012             this.activeItem = item;
15013             item.activate(autoExpand);
15014         }else if(autoExpand){
15015             item.expandMenu();
15016         }
15017     },
15018
15019     // private
15020     tryActivate : function(start, step){
15021         var items = this.items;
15022         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
15023             var item = items.get(i);
15024             if(!item.disabled && item.canActivate){
15025                 this.setActiveItem(item, false);
15026                 return item;
15027             }
15028         }
15029         return false;
15030     },
15031
15032     // private
15033     onMouseOver : function(e){
15034         var t;
15035         if(t = this.findTargetItem(e)){
15036             if(t.canActivate && !t.disabled){
15037                 this.setActiveItem(t, true);
15038             }
15039         }
15040         this.fireEvent("mouseover", this, e, t);
15041     },
15042
15043     // private
15044     onMouseOut : function(e){
15045         var t;
15046         if(t = this.findTargetItem(e)){
15047             if(t == this.activeItem && t.shouldDeactivate(e)){
15048                 this.activeItem.deactivate();
15049                 delete this.activeItem;
15050             }
15051         }
15052         this.fireEvent("mouseout", this, e, t);
15053     },
15054
15055     /**
15056      * Read-only.  Returns true if the menu is currently displayed, else false.
15057      * @type Boolean
15058      */
15059     isVisible : function(){
15060         return this.el && !this.hidden;
15061     },
15062
15063     /**
15064      * Displays this menu relative to another element
15065      * @param {String/HTMLElement/Roo.Element} element The element to align to
15066      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15067      * the element (defaults to this.defaultAlign)
15068      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15069      */
15070     show : function(el, pos, parentMenu){
15071         this.parentMenu = parentMenu;
15072         if(!this.el){
15073             this.render();
15074         }
15075         this.fireEvent("beforeshow", this);
15076         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15077     },
15078
15079     /**
15080      * Displays this menu at a specific xy position
15081      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15082      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15083      */
15084     showAt : function(xy, parentMenu, /* private: */_e){
15085         this.parentMenu = parentMenu;
15086         if(!this.el){
15087             this.render();
15088         }
15089         if(_e !== false){
15090             this.fireEvent("beforeshow", this);
15091             xy = this.el.adjustForConstraints(xy);
15092         }
15093         this.el.setXY(xy);
15094         this.el.show();
15095         this.hidden = false;
15096         this.focus();
15097         this.fireEvent("show", this);
15098     },
15099
15100     focus : function(){
15101         if(!this.hidden){
15102             this.doFocus.defer(50, this);
15103         }
15104     },
15105
15106     doFocus : function(){
15107         if(!this.hidden){
15108             this.focusEl.focus();
15109         }
15110     },
15111
15112     /**
15113      * Hides this menu and optionally all parent menus
15114      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15115      */
15116     hide : function(deep){
15117         if(this.el && this.isVisible()){
15118             this.fireEvent("beforehide", this);
15119             if(this.activeItem){
15120                 this.activeItem.deactivate();
15121                 this.activeItem = null;
15122             }
15123             this.el.hide();
15124             this.hidden = true;
15125             this.fireEvent("hide", this);
15126         }
15127         if(deep === true && this.parentMenu){
15128             this.parentMenu.hide(true);
15129         }
15130     },
15131
15132     /**
15133      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15134      * Any of the following are valid:
15135      * <ul>
15136      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15137      * <li>An HTMLElement object which will be converted to a menu item</li>
15138      * <li>A menu item config object that will be created as a new menu item</li>
15139      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15140      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15141      * </ul>
15142      * Usage:
15143      * <pre><code>
15144 // Create the menu
15145 var menu = new Roo.menu.Menu();
15146
15147 // Create a menu item to add by reference
15148 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15149
15150 // Add a bunch of items at once using different methods.
15151 // Only the last item added will be returned.
15152 var item = menu.add(
15153     menuItem,                // add existing item by ref
15154     'Dynamic Item',          // new TextItem
15155     '-',                     // new separator
15156     { text: 'Config Item' }  // new item by config
15157 );
15158 </code></pre>
15159      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15160      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15161      */
15162     add : function(){
15163         var a = arguments, l = a.length, item;
15164         for(var i = 0; i < l; i++){
15165             var el = a[i];
15166             if ((typeof(el) == "object") && el.xtype && el.xns) {
15167                 el = Roo.factory(el, Roo.menu);
15168             }
15169             
15170             if(el.render){ // some kind of Item
15171                 item = this.addItem(el);
15172             }else if(typeof el == "string"){ // string
15173                 if(el == "separator" || el == "-"){
15174                     item = this.addSeparator();
15175                 }else{
15176                     item = this.addText(el);
15177                 }
15178             }else if(el.tagName || el.el){ // element
15179                 item = this.addElement(el);
15180             }else if(typeof el == "object"){ // must be menu item config?
15181                 item = this.addMenuItem(el);
15182             }
15183         }
15184         return item;
15185     },
15186
15187     /**
15188      * Returns this menu's underlying {@link Roo.Element} object
15189      * @return {Roo.Element} The element
15190      */
15191     getEl : function(){
15192         if(!this.el){
15193             this.render();
15194         }
15195         return this.el;
15196     },
15197
15198     /**
15199      * Adds a separator bar to the menu
15200      * @return {Roo.menu.Item} The menu item that was added
15201      */
15202     addSeparator : function(){
15203         return this.addItem(new Roo.menu.Separator());
15204     },
15205
15206     /**
15207      * Adds an {@link Roo.Element} object to the menu
15208      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15209      * @return {Roo.menu.Item} The menu item that was added
15210      */
15211     addElement : function(el){
15212         return this.addItem(new Roo.menu.BaseItem(el));
15213     },
15214
15215     /**
15216      * Adds an existing object based on {@link Roo.menu.Item} to the menu
15217      * @param {Roo.menu.Item} item The menu item to add
15218      * @return {Roo.menu.Item} The menu item that was added
15219      */
15220     addItem : function(item){
15221         this.items.add(item);
15222         if(this.ul){
15223             var li = document.createElement("li");
15224             li.className = "x-menu-list-item";
15225             this.ul.dom.appendChild(li);
15226             item.render(li, this);
15227             this.delayAutoWidth();
15228         }
15229         return item;
15230     },
15231
15232     /**
15233      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15234      * @param {Object} config A MenuItem config object
15235      * @return {Roo.menu.Item} The menu item that was added
15236      */
15237     addMenuItem : function(config){
15238         if(!(config instanceof Roo.menu.Item)){
15239             if(typeof config.checked == "boolean"){ // must be check menu item config?
15240                 config = new Roo.menu.CheckItem(config);
15241             }else{
15242                 config = new Roo.menu.Item(config);
15243             }
15244         }
15245         return this.addItem(config);
15246     },
15247
15248     /**
15249      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15250      * @param {String} text The text to display in the menu item
15251      * @return {Roo.menu.Item} The menu item that was added
15252      */
15253     addText : function(text){
15254         return this.addItem(new Roo.menu.TextItem({ text : text }));
15255     },
15256
15257     /**
15258      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15259      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15260      * @param {Roo.menu.Item} item The menu item to add
15261      * @return {Roo.menu.Item} The menu item that was added
15262      */
15263     insert : function(index, item){
15264         this.items.insert(index, item);
15265         if(this.ul){
15266             var li = document.createElement("li");
15267             li.className = "x-menu-list-item";
15268             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15269             item.render(li, this);
15270             this.delayAutoWidth();
15271         }
15272         return item;
15273     },
15274
15275     /**
15276      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15277      * @param {Roo.menu.Item} item The menu item to remove
15278      */
15279     remove : function(item){
15280         this.items.removeKey(item.id);
15281         item.destroy();
15282     },
15283
15284     /**
15285      * Removes and destroys all items in the menu
15286      */
15287     removeAll : function(){
15288         var f;
15289         while(f = this.items.first()){
15290             this.remove(f);
15291         }
15292     }
15293 });
15294
15295 // MenuNav is a private utility class used internally by the Menu
15296 Roo.menu.MenuNav = function(menu){
15297     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15298     this.scope = this.menu = menu;
15299 };
15300
15301 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15302     doRelay : function(e, h){
15303         var k = e.getKey();
15304         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15305             this.menu.tryActivate(0, 1);
15306             return false;
15307         }
15308         return h.call(this.scope || this, e, this.menu);
15309     },
15310
15311     up : function(e, m){
15312         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15313             m.tryActivate(m.items.length-1, -1);
15314         }
15315     },
15316
15317     down : function(e, m){
15318         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15319             m.tryActivate(0, 1);
15320         }
15321     },
15322
15323     right : function(e, m){
15324         if(m.activeItem){
15325             m.activeItem.expandMenu(true);
15326         }
15327     },
15328
15329     left : function(e, m){
15330         m.hide();
15331         if(m.parentMenu && m.parentMenu.activeItem){
15332             m.parentMenu.activeItem.activate();
15333         }
15334     },
15335
15336     enter : function(e, m){
15337         if(m.activeItem){
15338             e.stopPropagation();
15339             m.activeItem.onClick(e);
15340             m.fireEvent("click", this, m.activeItem);
15341             return true;
15342         }
15343     }
15344 });/*
15345  * Based on:
15346  * Ext JS Library 1.1.1
15347  * Copyright(c) 2006-2007, Ext JS, LLC.
15348  *
15349  * Originally Released Under LGPL - original licence link has changed is not relivant.
15350  *
15351  * Fork - LGPL
15352  * <script type="text/javascript">
15353  */
15354  
15355 /**
15356  * @class Roo.menu.MenuMgr
15357  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15358  * @singleton
15359  */
15360 Roo.menu.MenuMgr = function(){
15361    var menus, active, groups = {}, attached = false, lastShow = new Date();
15362
15363    // private - called when first menu is created
15364    function init(){
15365        menus = {};
15366        active = new Roo.util.MixedCollection();
15367        Roo.get(document).addKeyListener(27, function(){
15368            if(active.length > 0){
15369                hideAll();
15370            }
15371        });
15372    }
15373
15374    // private
15375    function hideAll(){
15376        if(active && active.length > 0){
15377            var c = active.clone();
15378            c.each(function(m){
15379                m.hide();
15380            });
15381        }
15382    }
15383
15384    // private
15385    function onHide(m){
15386        active.remove(m);
15387        if(active.length < 1){
15388            Roo.get(document).un("mousedown", onMouseDown);
15389            attached = false;
15390        }
15391    }
15392
15393    // private
15394    function onShow(m){
15395        var last = active.last();
15396        lastShow = new Date();
15397        active.add(m);
15398        if(!attached){
15399            Roo.get(document).on("mousedown", onMouseDown);
15400            attached = true;
15401        }
15402        if(m.parentMenu){
15403           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15404           m.parentMenu.activeChild = m;
15405        }else if(last && last.isVisible()){
15406           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15407        }
15408    }
15409
15410    // private
15411    function onBeforeHide(m){
15412        if(m.activeChild){
15413            m.activeChild.hide();
15414        }
15415        if(m.autoHideTimer){
15416            clearTimeout(m.autoHideTimer);
15417            delete m.autoHideTimer;
15418        }
15419    }
15420
15421    // private
15422    function onBeforeShow(m){
15423        var pm = m.parentMenu;
15424        if(!pm && !m.allowOtherMenus){
15425            hideAll();
15426        }else if(pm && pm.activeChild && active != m){
15427            pm.activeChild.hide();
15428        }
15429    }
15430
15431    // private
15432    function onMouseDown(e){
15433        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15434            hideAll();
15435        }
15436    }
15437
15438    // private
15439    function onBeforeCheck(mi, state){
15440        if(state){
15441            var g = groups[mi.group];
15442            for(var i = 0, l = g.length; i < l; i++){
15443                if(g[i] != mi){
15444                    g[i].setChecked(false);
15445                }
15446            }
15447        }
15448    }
15449
15450    return {
15451
15452        /**
15453         * Hides all menus that are currently visible
15454         */
15455        hideAll : function(){
15456             hideAll();  
15457        },
15458
15459        // private
15460        register : function(menu){
15461            if(!menus){
15462                init();
15463            }
15464            menus[menu.id] = menu;
15465            menu.on("beforehide", onBeforeHide);
15466            menu.on("hide", onHide);
15467            menu.on("beforeshow", onBeforeShow);
15468            menu.on("show", onShow);
15469            var g = menu.group;
15470            if(g && menu.events["checkchange"]){
15471                if(!groups[g]){
15472                    groups[g] = [];
15473                }
15474                groups[g].push(menu);
15475                menu.on("checkchange", onCheck);
15476            }
15477        },
15478
15479         /**
15480          * Returns a {@link Roo.menu.Menu} object
15481          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15482          * be used to generate and return a new Menu instance.
15483          */
15484        get : function(menu){
15485            if(typeof menu == "string"){ // menu id
15486                return menus[menu];
15487            }else if(menu.events){  // menu instance
15488                return menu;
15489            }else if(typeof menu.length == 'number'){ // array of menu items?
15490                return new Roo.menu.Menu({items:menu});
15491            }else{ // otherwise, must be a config
15492                return new Roo.menu.Menu(menu);
15493            }
15494        },
15495
15496        // private
15497        unregister : function(menu){
15498            delete menus[menu.id];
15499            menu.un("beforehide", onBeforeHide);
15500            menu.un("hide", onHide);
15501            menu.un("beforeshow", onBeforeShow);
15502            menu.un("show", onShow);
15503            var g = menu.group;
15504            if(g && menu.events["checkchange"]){
15505                groups[g].remove(menu);
15506                menu.un("checkchange", onCheck);
15507            }
15508        },
15509
15510        // private
15511        registerCheckable : function(menuItem){
15512            var g = menuItem.group;
15513            if(g){
15514                if(!groups[g]){
15515                    groups[g] = [];
15516                }
15517                groups[g].push(menuItem);
15518                menuItem.on("beforecheckchange", onBeforeCheck);
15519            }
15520        },
15521
15522        // private
15523        unregisterCheckable : function(menuItem){
15524            var g = menuItem.group;
15525            if(g){
15526                groups[g].remove(menuItem);
15527                menuItem.un("beforecheckchange", onBeforeCheck);
15528            }
15529        }
15530    };
15531 }();/*
15532  * Based on:
15533  * Ext JS Library 1.1.1
15534  * Copyright(c) 2006-2007, Ext JS, LLC.
15535  *
15536  * Originally Released Under LGPL - original licence link has changed is not relivant.
15537  *
15538  * Fork - LGPL
15539  * <script type="text/javascript">
15540  */
15541  
15542
15543 /**
15544  * @class Roo.menu.BaseItem
15545  * @extends Roo.Component
15546  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15547  * management and base configuration options shared by all menu components.
15548  * @constructor
15549  * Creates a new BaseItem
15550  * @param {Object} config Configuration options
15551  */
15552 Roo.menu.BaseItem = function(config){
15553     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15554
15555     this.addEvents({
15556         /**
15557          * @event click
15558          * Fires when this item is clicked
15559          * @param {Roo.menu.BaseItem} this
15560          * @param {Roo.EventObject} e
15561          */
15562         click: true,
15563         /**
15564          * @event activate
15565          * Fires when this item is activated
15566          * @param {Roo.menu.BaseItem} this
15567          */
15568         activate : true,
15569         /**
15570          * @event deactivate
15571          * Fires when this item is deactivated
15572          * @param {Roo.menu.BaseItem} this
15573          */
15574         deactivate : true
15575     });
15576
15577     if(this.handler){
15578         this.on("click", this.handler, this.scope, true);
15579     }
15580 };
15581
15582 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15583     /**
15584      * @cfg {Function} handler
15585      * A function that will handle the click event of this menu item (defaults to undefined)
15586      */
15587     /**
15588      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15589      */
15590     canActivate : false,
15591     
15592      /**
15593      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15594      */
15595     hidden: false,
15596     
15597     /**
15598      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15599      */
15600     activeClass : "x-menu-item-active",
15601     /**
15602      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15603      */
15604     hideOnClick : true,
15605     /**
15606      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15607      */
15608     hideDelay : 100,
15609
15610     // private
15611     ctype: "Roo.menu.BaseItem",
15612
15613     // private
15614     actionMode : "container",
15615
15616     // private
15617     render : function(container, parentMenu){
15618         this.parentMenu = parentMenu;
15619         Roo.menu.BaseItem.superclass.render.call(this, container);
15620         this.container.menuItemId = this.id;
15621     },
15622
15623     // private
15624     onRender : function(container, position){
15625         this.el = Roo.get(this.el);
15626         container.dom.appendChild(this.el.dom);
15627     },
15628
15629     // private
15630     onClick : function(e){
15631         if(!this.disabled && this.fireEvent("click", this, e) !== false
15632                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15633             this.handleClick(e);
15634         }else{
15635             e.stopEvent();
15636         }
15637     },
15638
15639     // private
15640     activate : function(){
15641         if(this.disabled){
15642             return false;
15643         }
15644         var li = this.container;
15645         li.addClass(this.activeClass);
15646         this.region = li.getRegion().adjust(2, 2, -2, -2);
15647         this.fireEvent("activate", this);
15648         return true;
15649     },
15650
15651     // private
15652     deactivate : function(){
15653         this.container.removeClass(this.activeClass);
15654         this.fireEvent("deactivate", this);
15655     },
15656
15657     // private
15658     shouldDeactivate : function(e){
15659         return !this.region || !this.region.contains(e.getPoint());
15660     },
15661
15662     // private
15663     handleClick : function(e){
15664         if(this.hideOnClick){
15665             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15666         }
15667     },
15668
15669     // private
15670     expandMenu : function(autoActivate){
15671         // do nothing
15672     },
15673
15674     // private
15675     hideMenu : function(){
15676         // do nothing
15677     }
15678 });/*
15679  * Based on:
15680  * Ext JS Library 1.1.1
15681  * Copyright(c) 2006-2007, Ext JS, LLC.
15682  *
15683  * Originally Released Under LGPL - original licence link has changed is not relivant.
15684  *
15685  * Fork - LGPL
15686  * <script type="text/javascript">
15687  */
15688  
15689 /**
15690  * @class Roo.menu.Adapter
15691  * @extends Roo.menu.BaseItem
15692  * 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.
15693  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15694  * @constructor
15695  * Creates a new Adapter
15696  * @param {Object} config Configuration options
15697  */
15698 Roo.menu.Adapter = function(component, config){
15699     Roo.menu.Adapter.superclass.constructor.call(this, config);
15700     this.component = component;
15701 };
15702 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15703     // private
15704     canActivate : true,
15705
15706     // private
15707     onRender : function(container, position){
15708         this.component.render(container);
15709         this.el = this.component.getEl();
15710     },
15711
15712     // private
15713     activate : function(){
15714         if(this.disabled){
15715             return false;
15716         }
15717         this.component.focus();
15718         this.fireEvent("activate", this);
15719         return true;
15720     },
15721
15722     // private
15723     deactivate : function(){
15724         this.fireEvent("deactivate", this);
15725     },
15726
15727     // private
15728     disable : function(){
15729         this.component.disable();
15730         Roo.menu.Adapter.superclass.disable.call(this);
15731     },
15732
15733     // private
15734     enable : function(){
15735         this.component.enable();
15736         Roo.menu.Adapter.superclass.enable.call(this);
15737     }
15738 });/*
15739  * Based on:
15740  * Ext JS Library 1.1.1
15741  * Copyright(c) 2006-2007, Ext JS, LLC.
15742  *
15743  * Originally Released Under LGPL - original licence link has changed is not relivant.
15744  *
15745  * Fork - LGPL
15746  * <script type="text/javascript">
15747  */
15748
15749 /**
15750  * @class Roo.menu.TextItem
15751  * @extends Roo.menu.BaseItem
15752  * Adds a static text string to a menu, usually used as either a heading or group separator.
15753  * Note: old style constructor with text is still supported.
15754  * 
15755  * @constructor
15756  * Creates a new TextItem
15757  * @param {Object} cfg Configuration
15758  */
15759 Roo.menu.TextItem = function(cfg){
15760     if (typeof(cfg) == 'string') {
15761         this.text = cfg;
15762     } else {
15763         Roo.apply(this,cfg);
15764     }
15765     
15766     Roo.menu.TextItem.superclass.constructor.call(this);
15767 };
15768
15769 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15770     /**
15771      * @cfg {Boolean} text Text to show on item.
15772      */
15773     text : '',
15774     
15775     /**
15776      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15777      */
15778     hideOnClick : false,
15779     /**
15780      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15781      */
15782     itemCls : "x-menu-text",
15783
15784     // private
15785     onRender : function(){
15786         var s = document.createElement("span");
15787         s.className = this.itemCls;
15788         s.innerHTML = this.text;
15789         this.el = s;
15790         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15791     }
15792 });/*
15793  * Based on:
15794  * Ext JS Library 1.1.1
15795  * Copyright(c) 2006-2007, Ext JS, LLC.
15796  *
15797  * Originally Released Under LGPL - original licence link has changed is not relivant.
15798  *
15799  * Fork - LGPL
15800  * <script type="text/javascript">
15801  */
15802
15803 /**
15804  * @class Roo.menu.Separator
15805  * @extends Roo.menu.BaseItem
15806  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15807  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15808  * @constructor
15809  * @param {Object} config Configuration options
15810  */
15811 Roo.menu.Separator = function(config){
15812     Roo.menu.Separator.superclass.constructor.call(this, config);
15813 };
15814
15815 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15816     /**
15817      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15818      */
15819     itemCls : "x-menu-sep",
15820     /**
15821      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15822      */
15823     hideOnClick : false,
15824
15825     // private
15826     onRender : function(li){
15827         var s = document.createElement("span");
15828         s.className = this.itemCls;
15829         s.innerHTML = "&#160;";
15830         this.el = s;
15831         li.addClass("x-menu-sep-li");
15832         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15833     }
15834 });/*
15835  * Based on:
15836  * Ext JS Library 1.1.1
15837  * Copyright(c) 2006-2007, Ext JS, LLC.
15838  *
15839  * Originally Released Under LGPL - original licence link has changed is not relivant.
15840  *
15841  * Fork - LGPL
15842  * <script type="text/javascript">
15843  */
15844 /**
15845  * @class Roo.menu.Item
15846  * @extends Roo.menu.BaseItem
15847  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15848  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15849  * activation and click handling.
15850  * @constructor
15851  * Creates a new Item
15852  * @param {Object} config Configuration options
15853  */
15854 Roo.menu.Item = function(config){
15855     Roo.menu.Item.superclass.constructor.call(this, config);
15856     if(this.menu){
15857         this.menu = Roo.menu.MenuMgr.get(this.menu);
15858     }
15859 };
15860 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15861     
15862     /**
15863      * @cfg {String} text
15864      * The text to show on the menu item.
15865      */
15866     text: '',
15867      /**
15868      * @cfg {String} HTML to render in menu
15869      * The text to show on the menu item (HTML version).
15870      */
15871     html: '',
15872     /**
15873      * @cfg {String} icon
15874      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15875      */
15876     icon: undefined,
15877     /**
15878      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15879      */
15880     itemCls : "x-menu-item",
15881     /**
15882      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15883      */
15884     canActivate : true,
15885     /**
15886      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15887      */
15888     showDelay: 200,
15889     // doc'd in BaseItem
15890     hideDelay: 200,
15891
15892     // private
15893     ctype: "Roo.menu.Item",
15894     
15895     // private
15896     onRender : function(container, position){
15897         var el = document.createElement("a");
15898         el.hideFocus = true;
15899         el.unselectable = "on";
15900         el.href = this.href || "#";
15901         if(this.hrefTarget){
15902             el.target = this.hrefTarget;
15903         }
15904         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15905         
15906         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15907         
15908         el.innerHTML = String.format(
15909                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15910                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15911         this.el = el;
15912         Roo.menu.Item.superclass.onRender.call(this, container, position);
15913     },
15914
15915     /**
15916      * Sets the text to display in this menu item
15917      * @param {String} text The text to display
15918      * @param {Boolean} isHTML true to indicate text is pure html.
15919      */
15920     setText : function(text, isHTML){
15921         if (isHTML) {
15922             this.html = text;
15923         } else {
15924             this.text = text;
15925             this.html = '';
15926         }
15927         if(this.rendered){
15928             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15929      
15930             this.el.update(String.format(
15931                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15932                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15933             this.parentMenu.autoWidth();
15934         }
15935     },
15936
15937     // private
15938     handleClick : function(e){
15939         if(!this.href){ // if no link defined, stop the event automatically
15940             e.stopEvent();
15941         }
15942         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15943     },
15944
15945     // private
15946     activate : function(autoExpand){
15947         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15948             this.focus();
15949             if(autoExpand){
15950                 this.expandMenu();
15951             }
15952         }
15953         return true;
15954     },
15955
15956     // private
15957     shouldDeactivate : function(e){
15958         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15959             if(this.menu && this.menu.isVisible()){
15960                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15961             }
15962             return true;
15963         }
15964         return false;
15965     },
15966
15967     // private
15968     deactivate : function(){
15969         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15970         this.hideMenu();
15971     },
15972
15973     // private
15974     expandMenu : function(autoActivate){
15975         if(!this.disabled && this.menu){
15976             clearTimeout(this.hideTimer);
15977             delete this.hideTimer;
15978             if(!this.menu.isVisible() && !this.showTimer){
15979                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15980             }else if (this.menu.isVisible() && autoActivate){
15981                 this.menu.tryActivate(0, 1);
15982             }
15983         }
15984     },
15985
15986     // private
15987     deferExpand : function(autoActivate){
15988         delete this.showTimer;
15989         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15990         if(autoActivate){
15991             this.menu.tryActivate(0, 1);
15992         }
15993     },
15994
15995     // private
15996     hideMenu : function(){
15997         clearTimeout(this.showTimer);
15998         delete this.showTimer;
15999         if(!this.hideTimer && this.menu && this.menu.isVisible()){
16000             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
16001         }
16002     },
16003
16004     // private
16005     deferHide : function(){
16006         delete this.hideTimer;
16007         this.menu.hide();
16008     }
16009 });/*
16010  * Based on:
16011  * Ext JS Library 1.1.1
16012  * Copyright(c) 2006-2007, Ext JS, LLC.
16013  *
16014  * Originally Released Under LGPL - original licence link has changed is not relivant.
16015  *
16016  * Fork - LGPL
16017  * <script type="text/javascript">
16018  */
16019  
16020 /**
16021  * @class Roo.menu.CheckItem
16022  * @extends Roo.menu.Item
16023  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
16024  * @constructor
16025  * Creates a new CheckItem
16026  * @param {Object} config Configuration options
16027  */
16028 Roo.menu.CheckItem = function(config){
16029     Roo.menu.CheckItem.superclass.constructor.call(this, config);
16030     this.addEvents({
16031         /**
16032          * @event beforecheckchange
16033          * Fires before the checked value is set, providing an opportunity to cancel if needed
16034          * @param {Roo.menu.CheckItem} this
16035          * @param {Boolean} checked The new checked value that will be set
16036          */
16037         "beforecheckchange" : true,
16038         /**
16039          * @event checkchange
16040          * Fires after the checked value has been set
16041          * @param {Roo.menu.CheckItem} this
16042          * @param {Boolean} checked The checked value that was set
16043          */
16044         "checkchange" : true
16045     });
16046     if(this.checkHandler){
16047         this.on('checkchange', this.checkHandler, this.scope);
16048     }
16049 };
16050 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16051     /**
16052      * @cfg {String} group
16053      * All check items with the same group name will automatically be grouped into a single-select
16054      * radio button group (defaults to '')
16055      */
16056     /**
16057      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16058      */
16059     itemCls : "x-menu-item x-menu-check-item",
16060     /**
16061      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16062      */
16063     groupClass : "x-menu-group-item",
16064
16065     /**
16066      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
16067      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16068      * initialized with checked = true will be rendered as checked.
16069      */
16070     checked: false,
16071
16072     // private
16073     ctype: "Roo.menu.CheckItem",
16074
16075     // private
16076     onRender : function(c){
16077         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16078         if(this.group){
16079             this.el.addClass(this.groupClass);
16080         }
16081         Roo.menu.MenuMgr.registerCheckable(this);
16082         if(this.checked){
16083             this.checked = false;
16084             this.setChecked(true, true);
16085         }
16086     },
16087
16088     // private
16089     destroy : function(){
16090         if(this.rendered){
16091             Roo.menu.MenuMgr.unregisterCheckable(this);
16092         }
16093         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16094     },
16095
16096     /**
16097      * Set the checked state of this item
16098      * @param {Boolean} checked The new checked value
16099      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16100      */
16101     setChecked : function(state, suppressEvent){
16102         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16103             if(this.container){
16104                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16105             }
16106             this.checked = state;
16107             if(suppressEvent !== true){
16108                 this.fireEvent("checkchange", this, state);
16109             }
16110         }
16111     },
16112
16113     // private
16114     handleClick : function(e){
16115        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16116            this.setChecked(!this.checked);
16117        }
16118        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16119     }
16120 });/*
16121  * Based on:
16122  * Ext JS Library 1.1.1
16123  * Copyright(c) 2006-2007, Ext JS, LLC.
16124  *
16125  * Originally Released Under LGPL - original licence link has changed is not relivant.
16126  *
16127  * Fork - LGPL
16128  * <script type="text/javascript">
16129  */
16130  
16131 /**
16132  * @class Roo.menu.DateItem
16133  * @extends Roo.menu.Adapter
16134  * A menu item that wraps the {@link Roo.DatPicker} component.
16135  * @constructor
16136  * Creates a new DateItem
16137  * @param {Object} config Configuration options
16138  */
16139 Roo.menu.DateItem = function(config){
16140     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16141     /** The Roo.DatePicker object @type Roo.DatePicker */
16142     this.picker = this.component;
16143     this.addEvents({select: true});
16144     
16145     this.picker.on("render", function(picker){
16146         picker.getEl().swallowEvent("click");
16147         picker.container.addClass("x-menu-date-item");
16148     });
16149
16150     this.picker.on("select", this.onSelect, this);
16151 };
16152
16153 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16154     // private
16155     onSelect : function(picker, date){
16156         this.fireEvent("select", this, date, picker);
16157         Roo.menu.DateItem.superclass.handleClick.call(this);
16158     }
16159 });/*
16160  * Based on:
16161  * Ext JS Library 1.1.1
16162  * Copyright(c) 2006-2007, Ext JS, LLC.
16163  *
16164  * Originally Released Under LGPL - original licence link has changed is not relivant.
16165  *
16166  * Fork - LGPL
16167  * <script type="text/javascript">
16168  */
16169  
16170 /**
16171  * @class Roo.menu.ColorItem
16172  * @extends Roo.menu.Adapter
16173  * A menu item that wraps the {@link Roo.ColorPalette} component.
16174  * @constructor
16175  * Creates a new ColorItem
16176  * @param {Object} config Configuration options
16177  */
16178 Roo.menu.ColorItem = function(config){
16179     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16180     /** The Roo.ColorPalette object @type Roo.ColorPalette */
16181     this.palette = this.component;
16182     this.relayEvents(this.palette, ["select"]);
16183     if(this.selectHandler){
16184         this.on('select', this.selectHandler, this.scope);
16185     }
16186 };
16187 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16188  * Based on:
16189  * Ext JS Library 1.1.1
16190  * Copyright(c) 2006-2007, Ext JS, LLC.
16191  *
16192  * Originally Released Under LGPL - original licence link has changed is not relivant.
16193  *
16194  * Fork - LGPL
16195  * <script type="text/javascript">
16196  */
16197  
16198
16199 /**
16200  * @class Roo.menu.DateMenu
16201  * @extends Roo.menu.Menu
16202  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16203  * @constructor
16204  * Creates a new DateMenu
16205  * @param {Object} config Configuration options
16206  */
16207 Roo.menu.DateMenu = function(config){
16208     Roo.menu.DateMenu.superclass.constructor.call(this, config);
16209     this.plain = true;
16210     var di = new Roo.menu.DateItem(config);
16211     this.add(di);
16212     /**
16213      * The {@link Roo.DatePicker} instance for this DateMenu
16214      * @type DatePicker
16215      */
16216     this.picker = di.picker;
16217     /**
16218      * @event select
16219      * @param {DatePicker} picker
16220      * @param {Date} date
16221      */
16222     this.relayEvents(di, ["select"]);
16223     this.on('beforeshow', function(){
16224         if(this.picker){
16225             this.picker.hideMonthPicker(false);
16226         }
16227     }, this);
16228 };
16229 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16230     cls:'x-date-menu'
16231 });/*
16232  * Based on:
16233  * Ext JS Library 1.1.1
16234  * Copyright(c) 2006-2007, Ext JS, LLC.
16235  *
16236  * Originally Released Under LGPL - original licence link has changed is not relivant.
16237  *
16238  * Fork - LGPL
16239  * <script type="text/javascript">
16240  */
16241  
16242
16243 /**
16244  * @class Roo.menu.ColorMenu
16245  * @extends Roo.menu.Menu
16246  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16247  * @constructor
16248  * Creates a new ColorMenu
16249  * @param {Object} config Configuration options
16250  */
16251 Roo.menu.ColorMenu = function(config){
16252     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16253     this.plain = true;
16254     var ci = new Roo.menu.ColorItem(config);
16255     this.add(ci);
16256     /**
16257      * The {@link Roo.ColorPalette} instance for this ColorMenu
16258      * @type ColorPalette
16259      */
16260     this.palette = ci.palette;
16261     /**
16262      * @event select
16263      * @param {ColorPalette} palette
16264      * @param {String} color
16265      */
16266     this.relayEvents(ci, ["select"]);
16267 };
16268 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
16269  * Based on:
16270  * Ext JS Library 1.1.1
16271  * Copyright(c) 2006-2007, Ext JS, LLC.
16272  *
16273  * Originally Released Under LGPL - original licence link has changed is not relivant.
16274  *
16275  * Fork - LGPL
16276  * <script type="text/javascript">
16277  */
16278  
16279 /**
16280  * @class Roo.form.TextItem
16281  * @extends Roo.BoxComponent
16282  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16283  * @constructor
16284  * Creates a new TextItem
16285  * @param {Object} config Configuration options
16286  */
16287 Roo.form.TextItem = function(config){
16288     Roo.form.TextItem.superclass.constructor.call(this, config);
16289 };
16290
16291 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
16292     
16293     /**
16294      * @cfg {String} tag the tag for this item (default div)
16295      */
16296     tag : 'div',
16297     /**
16298      * @cfg {String} html the content for this item
16299      */
16300     html : '',
16301     
16302     getAutoCreate : function()
16303     {
16304         var cfg = {
16305             id: this.id,
16306             tag: this.tag,
16307             html: this.html,
16308             cls: 'x-form-item'
16309         };
16310         
16311         return cfg;
16312         
16313     },
16314     
16315     onRender : function(ct, position)
16316     {
16317         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
16318         
16319         if(!this.el){
16320             var cfg = this.getAutoCreate();
16321             if(!cfg.name){
16322                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16323             }
16324             if (!cfg.name.length) {
16325                 delete cfg.name;
16326             }
16327             this.el = ct.createChild(cfg, position);
16328         }
16329     }
16330     
16331 });/*
16332  * Based on:
16333  * Ext JS Library 1.1.1
16334  * Copyright(c) 2006-2007, Ext JS, LLC.
16335  *
16336  * Originally Released Under LGPL - original licence link has changed is not relivant.
16337  *
16338  * Fork - LGPL
16339  * <script type="text/javascript">
16340  */
16341  
16342 /**
16343  * @class Roo.form.Field
16344  * @extends Roo.BoxComponent
16345  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16346  * @constructor
16347  * Creates a new Field
16348  * @param {Object} config Configuration options
16349  */
16350 Roo.form.Field = function(config){
16351     Roo.form.Field.superclass.constructor.call(this, config);
16352 };
16353
16354 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16355     /**
16356      * @cfg {String} fieldLabel Label to use when rendering a form.
16357      */
16358        /**
16359      * @cfg {String} qtip Mouse over tip
16360      */
16361      
16362     /**
16363      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16364      */
16365     invalidClass : "x-form-invalid",
16366     /**
16367      * @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")
16368      */
16369     invalidText : "The value in this field is invalid",
16370     /**
16371      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16372      */
16373     focusClass : "x-form-focus",
16374     /**
16375      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16376       automatic validation (defaults to "keyup").
16377      */
16378     validationEvent : "keyup",
16379     /**
16380      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16381      */
16382     validateOnBlur : true,
16383     /**
16384      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16385      */
16386     validationDelay : 250,
16387     /**
16388      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16389      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16390      */
16391     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16392     /**
16393      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16394      */
16395     fieldClass : "x-form-field",
16396     /**
16397      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16398      *<pre>
16399 Value         Description
16400 -----------   ----------------------------------------------------------------------
16401 qtip          Display a quick tip when the user hovers over the field
16402 title         Display a default browser title attribute popup
16403 under         Add a block div beneath the field containing the error text
16404 side          Add an error icon to the right of the field with a popup on hover
16405 [element id]  Add the error text directly to the innerHTML of the specified element
16406 </pre>
16407      */
16408     msgTarget : 'qtip',
16409     /**
16410      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16411      */
16412     msgFx : 'normal',
16413
16414     /**
16415      * @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.
16416      */
16417     readOnly : false,
16418
16419     /**
16420      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16421      */
16422     disabled : false,
16423
16424     /**
16425      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16426      */
16427     inputType : undefined,
16428     
16429     /**
16430      * @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).
16431          */
16432         tabIndex : undefined,
16433         
16434     // private
16435     isFormField : true,
16436
16437     // private
16438     hasFocus : false,
16439     /**
16440      * @property {Roo.Element} fieldEl
16441      * Element Containing the rendered Field (with label etc.)
16442      */
16443     /**
16444      * @cfg {Mixed} value A value to initialize this field with.
16445      */
16446     value : undefined,
16447
16448     /**
16449      * @cfg {String} name The field's HTML name attribute.
16450      */
16451     /**
16452      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16453      */
16454     // private
16455     loadedValue : false,
16456      
16457      
16458         // private ??
16459         initComponent : function(){
16460         Roo.form.Field.superclass.initComponent.call(this);
16461         this.addEvents({
16462             /**
16463              * @event focus
16464              * Fires when this field receives input focus.
16465              * @param {Roo.form.Field} this
16466              */
16467             focus : true,
16468             /**
16469              * @event blur
16470              * Fires when this field loses input focus.
16471              * @param {Roo.form.Field} this
16472              */
16473             blur : true,
16474             /**
16475              * @event specialkey
16476              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16477              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16478              * @param {Roo.form.Field} this
16479              * @param {Roo.EventObject} e The event object
16480              */
16481             specialkey : true,
16482             /**
16483              * @event change
16484              * Fires just before the field blurs if the field value has changed.
16485              * @param {Roo.form.Field} this
16486              * @param {Mixed} newValue The new value
16487              * @param {Mixed} oldValue The original value
16488              */
16489             change : true,
16490             /**
16491              * @event invalid
16492              * Fires after the field has been marked as invalid.
16493              * @param {Roo.form.Field} this
16494              * @param {String} msg The validation message
16495              */
16496             invalid : true,
16497             /**
16498              * @event valid
16499              * Fires after the field has been validated with no errors.
16500              * @param {Roo.form.Field} this
16501              */
16502             valid : true,
16503              /**
16504              * @event keyup
16505              * Fires after the key up
16506              * @param {Roo.form.Field} this
16507              * @param {Roo.EventObject}  e The event Object
16508              */
16509             keyup : true
16510         });
16511     },
16512
16513     /**
16514      * Returns the name attribute of the field if available
16515      * @return {String} name The field name
16516      */
16517     getName: function(){
16518          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16519     },
16520
16521     // private
16522     onRender : function(ct, position){
16523         Roo.form.Field.superclass.onRender.call(this, ct, position);
16524         if(!this.el){
16525             var cfg = this.getAutoCreate();
16526             if(!cfg.name){
16527                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16528             }
16529             if (!cfg.name.length) {
16530                 delete cfg.name;
16531             }
16532             if(this.inputType){
16533                 cfg.type = this.inputType;
16534             }
16535             this.el = ct.createChild(cfg, position);
16536         }
16537         var type = this.el.dom.type;
16538         if(type){
16539             if(type == 'password'){
16540                 type = 'text';
16541             }
16542             this.el.addClass('x-form-'+type);
16543         }
16544         if(this.readOnly){
16545             this.el.dom.readOnly = true;
16546         }
16547         if(this.tabIndex !== undefined){
16548             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16549         }
16550
16551         this.el.addClass([this.fieldClass, this.cls]);
16552         this.initValue();
16553     },
16554
16555     /**
16556      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16557      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16558      * @return {Roo.form.Field} this
16559      */
16560     applyTo : function(target){
16561         this.allowDomMove = false;
16562         this.el = Roo.get(target);
16563         this.render(this.el.dom.parentNode);
16564         return this;
16565     },
16566
16567     // private
16568     initValue : function(){
16569         if(this.value !== undefined){
16570             this.setValue(this.value);
16571         }else if(this.el.dom.value.length > 0){
16572             this.setValue(this.el.dom.value);
16573         }
16574     },
16575
16576     /**
16577      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16578      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16579      */
16580     isDirty : function() {
16581         if(this.disabled) {
16582             return false;
16583         }
16584         return String(this.getValue()) !== String(this.originalValue);
16585     },
16586
16587     /**
16588      * stores the current value in loadedValue
16589      */
16590     resetHasChanged : function()
16591     {
16592         this.loadedValue = String(this.getValue());
16593     },
16594     /**
16595      * checks the current value against the 'loaded' value.
16596      * Note - will return false if 'resetHasChanged' has not been called first.
16597      */
16598     hasChanged : function()
16599     {
16600         if(this.disabled || this.readOnly) {
16601             return false;
16602         }
16603         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16604     },
16605     
16606     
16607     
16608     // private
16609     afterRender : function(){
16610         Roo.form.Field.superclass.afterRender.call(this);
16611         this.initEvents();
16612     },
16613
16614     // private
16615     fireKey : function(e){
16616         //Roo.log('field ' + e.getKey());
16617         if(e.isNavKeyPress()){
16618             this.fireEvent("specialkey", this, e);
16619         }
16620     },
16621
16622     /**
16623      * Resets the current field value to the originally loaded value and clears any validation messages
16624      */
16625     reset : function(){
16626         this.setValue(this.resetValue);
16627         this.originalValue = this.getValue();
16628         this.clearInvalid();
16629     },
16630
16631     // private
16632     initEvents : function(){
16633         // safari killled keypress - so keydown is now used..
16634         this.el.on("keydown" , this.fireKey,  this);
16635         this.el.on("focus", this.onFocus,  this);
16636         this.el.on("blur", this.onBlur,  this);
16637         this.el.relayEvent('keyup', this);
16638
16639         // reference to original value for reset
16640         this.originalValue = this.getValue();
16641         this.resetValue =  this.getValue();
16642     },
16643
16644     // private
16645     onFocus : function(){
16646         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16647             this.el.addClass(this.focusClass);
16648         }
16649         if(!this.hasFocus){
16650             this.hasFocus = true;
16651             this.startValue = this.getValue();
16652             this.fireEvent("focus", this);
16653         }
16654     },
16655
16656     beforeBlur : Roo.emptyFn,
16657
16658     // private
16659     onBlur : function(){
16660         this.beforeBlur();
16661         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16662             this.el.removeClass(this.focusClass);
16663         }
16664         this.hasFocus = false;
16665         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16666             this.validate();
16667         }
16668         var v = this.getValue();
16669         if(String(v) !== String(this.startValue)){
16670             this.fireEvent('change', this, v, this.startValue);
16671         }
16672         this.fireEvent("blur", this);
16673     },
16674
16675     /**
16676      * Returns whether or not the field value is currently valid
16677      * @param {Boolean} preventMark True to disable marking the field invalid
16678      * @return {Boolean} True if the value is valid, else false
16679      */
16680     isValid : function(preventMark){
16681         if(this.disabled){
16682             return true;
16683         }
16684         var restore = this.preventMark;
16685         this.preventMark = preventMark === true;
16686         var v = this.validateValue(this.processValue(this.getRawValue()));
16687         this.preventMark = restore;
16688         return v;
16689     },
16690
16691     /**
16692      * Validates the field value
16693      * @return {Boolean} True if the value is valid, else false
16694      */
16695     validate : function(){
16696         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16697             this.clearInvalid();
16698             return true;
16699         }
16700         return false;
16701     },
16702
16703     processValue : function(value){
16704         return value;
16705     },
16706
16707     // private
16708     // Subclasses should provide the validation implementation by overriding this
16709     validateValue : function(value){
16710         return true;
16711     },
16712
16713     /**
16714      * Mark this field as invalid
16715      * @param {String} msg The validation message
16716      */
16717     markInvalid : function(msg){
16718         if(!this.rendered || this.preventMark){ // not rendered
16719             return;
16720         }
16721         
16722         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16723         
16724         obj.el.addClass(this.invalidClass);
16725         msg = msg || this.invalidText;
16726         switch(this.msgTarget){
16727             case 'qtip':
16728                 obj.el.dom.qtip = msg;
16729                 obj.el.dom.qclass = 'x-form-invalid-tip';
16730                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16731                     Roo.QuickTips.enable();
16732                 }
16733                 break;
16734             case 'title':
16735                 this.el.dom.title = msg;
16736                 break;
16737             case 'under':
16738                 if(!this.errorEl){
16739                     var elp = this.el.findParent('.x-form-element', 5, true);
16740                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16741                     this.errorEl.setWidth(elp.getWidth(true)-20);
16742                 }
16743                 this.errorEl.update(msg);
16744                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16745                 break;
16746             case 'side':
16747                 if(!this.errorIcon){
16748                     var elp = this.el.findParent('.x-form-element', 5, true);
16749                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16750                 }
16751                 this.alignErrorIcon();
16752                 this.errorIcon.dom.qtip = msg;
16753                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16754                 this.errorIcon.show();
16755                 this.on('resize', this.alignErrorIcon, this);
16756                 break;
16757             default:
16758                 var t = Roo.getDom(this.msgTarget);
16759                 t.innerHTML = msg;
16760                 t.style.display = this.msgDisplay;
16761                 break;
16762         }
16763         this.fireEvent('invalid', this, msg);
16764     },
16765
16766     // private
16767     alignErrorIcon : function(){
16768         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16769     },
16770
16771     /**
16772      * Clear any invalid styles/messages for this field
16773      */
16774     clearInvalid : function(){
16775         if(!this.rendered || this.preventMark){ // not rendered
16776             return;
16777         }
16778         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16779         
16780         obj.el.removeClass(this.invalidClass);
16781         switch(this.msgTarget){
16782             case 'qtip':
16783                 obj.el.dom.qtip = '';
16784                 break;
16785             case 'title':
16786                 this.el.dom.title = '';
16787                 break;
16788             case 'under':
16789                 if(this.errorEl){
16790                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16791                 }
16792                 break;
16793             case 'side':
16794                 if(this.errorIcon){
16795                     this.errorIcon.dom.qtip = '';
16796                     this.errorIcon.hide();
16797                     this.un('resize', this.alignErrorIcon, this);
16798                 }
16799                 break;
16800             default:
16801                 var t = Roo.getDom(this.msgTarget);
16802                 t.innerHTML = '';
16803                 t.style.display = 'none';
16804                 break;
16805         }
16806         this.fireEvent('valid', this);
16807     },
16808
16809     /**
16810      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16811      * @return {Mixed} value The field value
16812      */
16813     getRawValue : function(){
16814         var v = this.el.getValue();
16815         
16816         return v;
16817     },
16818
16819     /**
16820      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16821      * @return {Mixed} value The field value
16822      */
16823     getValue : function(){
16824         var v = this.el.getValue();
16825          
16826         return v;
16827     },
16828
16829     /**
16830      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16831      * @param {Mixed} value The value to set
16832      */
16833     setRawValue : function(v){
16834         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16835     },
16836
16837     /**
16838      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16839      * @param {Mixed} value The value to set
16840      */
16841     setValue : function(v){
16842         this.value = v;
16843         if(this.rendered){
16844             this.el.dom.value = (v === null || v === undefined ? '' : v);
16845              this.validate();
16846         }
16847     },
16848
16849     adjustSize : function(w, h){
16850         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16851         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16852         return s;
16853     },
16854
16855     adjustWidth : function(tag, w){
16856         tag = tag.toLowerCase();
16857         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16858             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16859                 if(tag == 'input'){
16860                     return w + 2;
16861                 }
16862                 if(tag == 'textarea'){
16863                     return w-2;
16864                 }
16865             }else if(Roo.isOpera){
16866                 if(tag == 'input'){
16867                     return w + 2;
16868                 }
16869                 if(tag == 'textarea'){
16870                     return w-2;
16871                 }
16872             }
16873         }
16874         return w;
16875     }
16876 });
16877
16878
16879 // anything other than normal should be considered experimental
16880 Roo.form.Field.msgFx = {
16881     normal : {
16882         show: function(msgEl, f){
16883             msgEl.setDisplayed('block');
16884         },
16885
16886         hide : function(msgEl, f){
16887             msgEl.setDisplayed(false).update('');
16888         }
16889     },
16890
16891     slide : {
16892         show: function(msgEl, f){
16893             msgEl.slideIn('t', {stopFx:true});
16894         },
16895
16896         hide : function(msgEl, f){
16897             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16898         }
16899     },
16900
16901     slideRight : {
16902         show: function(msgEl, f){
16903             msgEl.fixDisplay();
16904             msgEl.alignTo(f.el, 'tl-tr');
16905             msgEl.slideIn('l', {stopFx:true});
16906         },
16907
16908         hide : function(msgEl, f){
16909             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16910         }
16911     }
16912 };/*
16913  * Based on:
16914  * Ext JS Library 1.1.1
16915  * Copyright(c) 2006-2007, Ext JS, LLC.
16916  *
16917  * Originally Released Under LGPL - original licence link has changed is not relivant.
16918  *
16919  * Fork - LGPL
16920  * <script type="text/javascript">
16921  */
16922  
16923
16924 /**
16925  * @class Roo.form.TextField
16926  * @extends Roo.form.Field
16927  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16928  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16929  * @constructor
16930  * Creates a new TextField
16931  * @param {Object} config Configuration options
16932  */
16933 Roo.form.TextField = function(config){
16934     Roo.form.TextField.superclass.constructor.call(this, config);
16935     this.addEvents({
16936         /**
16937          * @event autosize
16938          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16939          * according to the default logic, but this event provides a hook for the developer to apply additional
16940          * logic at runtime to resize the field if needed.
16941              * @param {Roo.form.Field} this This text field
16942              * @param {Number} width The new field width
16943              */
16944         autosize : true
16945     });
16946 };
16947
16948 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16949     /**
16950      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16951      */
16952     grow : false,
16953     /**
16954      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16955      */
16956     growMin : 30,
16957     /**
16958      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16959      */
16960     growMax : 800,
16961     /**
16962      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16963      */
16964     vtype : null,
16965     /**
16966      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16967      */
16968     maskRe : null,
16969     /**
16970      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16971      */
16972     disableKeyFilter : false,
16973     /**
16974      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16975      */
16976     allowBlank : true,
16977     /**
16978      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16979      */
16980     minLength : 0,
16981     /**
16982      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16983      */
16984     maxLength : Number.MAX_VALUE,
16985     /**
16986      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16987      */
16988     minLengthText : "The minimum length for this field is {0}",
16989     /**
16990      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16991      */
16992     maxLengthText : "The maximum length for this field is {0}",
16993     /**
16994      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16995      */
16996     selectOnFocus : false,
16997     /**
16998      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16999      */
17000     blankText : "This field is required",
17001     /**
17002      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
17003      * If available, this function will be called only after the basic validators all return true, and will be passed the
17004      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
17005      */
17006     validator : null,
17007     /**
17008      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
17009      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
17010      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
17011      */
17012     regex : null,
17013     /**
17014      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
17015      */
17016     regexText : "",
17017     /**
17018      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
17019      */
17020     emptyText : null,
17021    
17022
17023     // private
17024     initEvents : function()
17025     {
17026         if (this.emptyText) {
17027             this.el.attr('placeholder', this.emptyText);
17028         }
17029         
17030         Roo.form.TextField.superclass.initEvents.call(this);
17031         if(this.validationEvent == 'keyup'){
17032             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
17033             this.el.on('keyup', this.filterValidation, this);
17034         }
17035         else if(this.validationEvent !== false){
17036             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
17037         }
17038         
17039         if(this.selectOnFocus){
17040             this.on("focus", this.preFocus, this);
17041             
17042         }
17043         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
17044             this.el.on("keypress", this.filterKeys, this);
17045         }
17046         if(this.grow){
17047             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
17048             this.el.on("click", this.autoSize,  this);
17049         }
17050         if(this.el.is('input[type=password]') && Roo.isSafari){
17051             this.el.on('keydown', this.SafariOnKeyDown, this);
17052         }
17053     },
17054
17055     processValue : function(value){
17056         if(this.stripCharsRe){
17057             var newValue = value.replace(this.stripCharsRe, '');
17058             if(newValue !== value){
17059                 this.setRawValue(newValue);
17060                 return newValue;
17061             }
17062         }
17063         return value;
17064     },
17065
17066     filterValidation : function(e){
17067         if(!e.isNavKeyPress()){
17068             this.validationTask.delay(this.validationDelay);
17069         }
17070     },
17071
17072     // private
17073     onKeyUp : function(e){
17074         if(!e.isNavKeyPress()){
17075             this.autoSize();
17076         }
17077     },
17078
17079     /**
17080      * Resets the current field value to the originally-loaded value and clears any validation messages.
17081      *  
17082      */
17083     reset : function(){
17084         Roo.form.TextField.superclass.reset.call(this);
17085        
17086     },
17087
17088     
17089     // private
17090     preFocus : function(){
17091         
17092         if(this.selectOnFocus){
17093             this.el.dom.select();
17094         }
17095     },
17096
17097     
17098     // private
17099     filterKeys : function(e){
17100         var k = e.getKey();
17101         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
17102             return;
17103         }
17104         var c = e.getCharCode(), cc = String.fromCharCode(c);
17105         if(Roo.isIE && (e.isSpecialKey() || !cc)){
17106             return;
17107         }
17108         if(!this.maskRe.test(cc)){
17109             e.stopEvent();
17110         }
17111     },
17112
17113     setValue : function(v){
17114         
17115         Roo.form.TextField.superclass.setValue.apply(this, arguments);
17116         
17117         this.autoSize();
17118     },
17119
17120     /**
17121      * Validates a value according to the field's validation rules and marks the field as invalid
17122      * if the validation fails
17123      * @param {Mixed} value The value to validate
17124      * @return {Boolean} True if the value is valid, else false
17125      */
17126     validateValue : function(value){
17127         if(value.length < 1)  { // if it's blank
17128              if(this.allowBlank){
17129                 this.clearInvalid();
17130                 return true;
17131              }else{
17132                 this.markInvalid(this.blankText);
17133                 return false;
17134              }
17135         }
17136         if(value.length < this.minLength){
17137             this.markInvalid(String.format(this.minLengthText, this.minLength));
17138             return false;
17139         }
17140         if(value.length > this.maxLength){
17141             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17142             return false;
17143         }
17144         if(this.vtype){
17145             var vt = Roo.form.VTypes;
17146             if(!vt[this.vtype](value, this)){
17147                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17148                 return false;
17149             }
17150         }
17151         if(typeof this.validator == "function"){
17152             var msg = this.validator(value);
17153             if(msg !== true){
17154                 this.markInvalid(msg);
17155                 return false;
17156             }
17157         }
17158         if(this.regex && !this.regex.test(value)){
17159             this.markInvalid(this.regexText);
17160             return false;
17161         }
17162         return true;
17163     },
17164
17165     /**
17166      * Selects text in this field
17167      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17168      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17169      */
17170     selectText : function(start, end){
17171         var v = this.getRawValue();
17172         if(v.length > 0){
17173             start = start === undefined ? 0 : start;
17174             end = end === undefined ? v.length : end;
17175             var d = this.el.dom;
17176             if(d.setSelectionRange){
17177                 d.setSelectionRange(start, end);
17178             }else if(d.createTextRange){
17179                 var range = d.createTextRange();
17180                 range.moveStart("character", start);
17181                 range.moveEnd("character", v.length-end);
17182                 range.select();
17183             }
17184         }
17185     },
17186
17187     /**
17188      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17189      * This only takes effect if grow = true, and fires the autosize event.
17190      */
17191     autoSize : function(){
17192         if(!this.grow || !this.rendered){
17193             return;
17194         }
17195         if(!this.metrics){
17196             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17197         }
17198         var el = this.el;
17199         var v = el.dom.value;
17200         var d = document.createElement('div');
17201         d.appendChild(document.createTextNode(v));
17202         v = d.innerHTML;
17203         d = null;
17204         v += "&#160;";
17205         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17206         this.el.setWidth(w);
17207         this.fireEvent("autosize", this, w);
17208     },
17209     
17210     // private
17211     SafariOnKeyDown : function(event)
17212     {
17213         // this is a workaround for a password hang bug on chrome/ webkit.
17214         
17215         var isSelectAll = false;
17216         
17217         if(this.el.dom.selectionEnd > 0){
17218             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17219         }
17220         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17221             event.preventDefault();
17222             this.setValue('');
17223             return;
17224         }
17225         
17226         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17227             
17228             event.preventDefault();
17229             // this is very hacky as keydown always get's upper case.
17230             
17231             var cc = String.fromCharCode(event.getCharCode());
17232             
17233             
17234             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
17235             
17236         }
17237         
17238         
17239     }
17240 });/*
17241  * Based on:
17242  * Ext JS Library 1.1.1
17243  * Copyright(c) 2006-2007, Ext JS, LLC.
17244  *
17245  * Originally Released Under LGPL - original licence link has changed is not relivant.
17246  *
17247  * Fork - LGPL
17248  * <script type="text/javascript">
17249  */
17250  
17251 /**
17252  * @class Roo.form.Hidden
17253  * @extends Roo.form.TextField
17254  * Simple Hidden element used on forms 
17255  * 
17256  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17257  * 
17258  * @constructor
17259  * Creates a new Hidden form element.
17260  * @param {Object} config Configuration options
17261  */
17262
17263
17264
17265 // easy hidden field...
17266 Roo.form.Hidden = function(config){
17267     Roo.form.Hidden.superclass.constructor.call(this, config);
17268 };
17269   
17270 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17271     fieldLabel:      '',
17272     inputType:      'hidden',
17273     width:          50,
17274     allowBlank:     true,
17275     labelSeparator: '',
17276     hidden:         true,
17277     itemCls :       'x-form-item-display-none'
17278
17279
17280 });
17281
17282
17283 /*
17284  * Based on:
17285  * Ext JS Library 1.1.1
17286  * Copyright(c) 2006-2007, Ext JS, LLC.
17287  *
17288  * Originally Released Under LGPL - original licence link has changed is not relivant.
17289  *
17290  * Fork - LGPL
17291  * <script type="text/javascript">
17292  */
17293  
17294 /**
17295  * @class Roo.form.TriggerField
17296  * @extends Roo.form.TextField
17297  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17298  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17299  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17300  * for which you can provide a custom implementation.  For example:
17301  * <pre><code>
17302 var trigger = new Roo.form.TriggerField();
17303 trigger.onTriggerClick = myTriggerFn;
17304 trigger.applyTo('my-field');
17305 </code></pre>
17306  *
17307  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17308  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17309  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
17310  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17311  * @constructor
17312  * Create a new TriggerField.
17313  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17314  * to the base TextField)
17315  */
17316 Roo.form.TriggerField = function(config){
17317     this.mimicing = false;
17318     Roo.form.TriggerField.superclass.constructor.call(this, config);
17319 };
17320
17321 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17322     /**
17323      * @cfg {String} triggerClass A CSS class to apply to the trigger
17324      */
17325     /**
17326      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17327      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17328      */
17329     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17330     /**
17331      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17332      */
17333     hideTrigger:false,
17334
17335     /** @cfg {Boolean} grow @hide */
17336     /** @cfg {Number} growMin @hide */
17337     /** @cfg {Number} growMax @hide */
17338
17339     /**
17340      * @hide 
17341      * @method
17342      */
17343     autoSize: Roo.emptyFn,
17344     // private
17345     monitorTab : true,
17346     // private
17347     deferHeight : true,
17348
17349     
17350     actionMode : 'wrap',
17351     // private
17352     onResize : function(w, h){
17353         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17354         if(typeof w == 'number'){
17355             var x = w - this.trigger.getWidth();
17356             this.el.setWidth(this.adjustWidth('input', x));
17357             this.trigger.setStyle('left', x+'px');
17358         }
17359     },
17360
17361     // private
17362     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17363
17364     // private
17365     getResizeEl : function(){
17366         return this.wrap;
17367     },
17368
17369     // private
17370     getPositionEl : function(){
17371         return this.wrap;
17372     },
17373
17374     // private
17375     alignErrorIcon : function(){
17376         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17377     },
17378
17379     // private
17380     onRender : function(ct, position){
17381         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17382         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17383         this.trigger = this.wrap.createChild(this.triggerConfig ||
17384                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17385         if(this.hideTrigger){
17386             this.trigger.setDisplayed(false);
17387         }
17388         this.initTrigger();
17389         if(!this.width){
17390             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17391         }
17392     },
17393
17394     // private
17395     initTrigger : function(){
17396         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17397         this.trigger.addClassOnOver('x-form-trigger-over');
17398         this.trigger.addClassOnClick('x-form-trigger-click');
17399     },
17400
17401     // private
17402     onDestroy : function(){
17403         if(this.trigger){
17404             this.trigger.removeAllListeners();
17405             this.trigger.remove();
17406         }
17407         if(this.wrap){
17408             this.wrap.remove();
17409         }
17410         Roo.form.TriggerField.superclass.onDestroy.call(this);
17411     },
17412
17413     // private
17414     onFocus : function(){
17415         Roo.form.TriggerField.superclass.onFocus.call(this);
17416         if(!this.mimicing){
17417             this.wrap.addClass('x-trigger-wrap-focus');
17418             this.mimicing = true;
17419             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17420             if(this.monitorTab){
17421                 this.el.on("keydown", this.checkTab, this);
17422             }
17423         }
17424     },
17425
17426     // private
17427     checkTab : function(e){
17428         if(e.getKey() == e.TAB){
17429             this.triggerBlur();
17430         }
17431     },
17432
17433     // private
17434     onBlur : function(){
17435         // do nothing
17436     },
17437
17438     // private
17439     mimicBlur : function(e, t){
17440         if(!this.wrap.contains(t) && this.validateBlur()){
17441             this.triggerBlur();
17442         }
17443     },
17444
17445     // private
17446     triggerBlur : function(){
17447         this.mimicing = false;
17448         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17449         if(this.monitorTab){
17450             this.el.un("keydown", this.checkTab, this);
17451         }
17452         this.wrap.removeClass('x-trigger-wrap-focus');
17453         Roo.form.TriggerField.superclass.onBlur.call(this);
17454     },
17455
17456     // private
17457     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17458     validateBlur : function(e, t){
17459         return true;
17460     },
17461
17462     // private
17463     onDisable : function(){
17464         Roo.form.TriggerField.superclass.onDisable.call(this);
17465         if(this.wrap){
17466             this.wrap.addClass('x-item-disabled');
17467         }
17468     },
17469
17470     // private
17471     onEnable : function(){
17472         Roo.form.TriggerField.superclass.onEnable.call(this);
17473         if(this.wrap){
17474             this.wrap.removeClass('x-item-disabled');
17475         }
17476     },
17477
17478     // private
17479     onShow : function(){
17480         var ae = this.getActionEl();
17481         
17482         if(ae){
17483             ae.dom.style.display = '';
17484             ae.dom.style.visibility = 'visible';
17485         }
17486     },
17487
17488     // private
17489     
17490     onHide : function(){
17491         var ae = this.getActionEl();
17492         ae.dom.style.display = 'none';
17493     },
17494
17495     /**
17496      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17497      * by an implementing function.
17498      * @method
17499      * @param {EventObject} e
17500      */
17501     onTriggerClick : Roo.emptyFn
17502 });
17503
17504 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17505 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17506 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17507 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17508     initComponent : function(){
17509         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17510
17511         this.triggerConfig = {
17512             tag:'span', cls:'x-form-twin-triggers', cn:[
17513             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17514             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17515         ]};
17516     },
17517
17518     getTrigger : function(index){
17519         return this.triggers[index];
17520     },
17521
17522     initTrigger : function(){
17523         var ts = this.trigger.select('.x-form-trigger', true);
17524         this.wrap.setStyle('overflow', 'hidden');
17525         var triggerField = this;
17526         ts.each(function(t, all, index){
17527             t.hide = function(){
17528                 var w = triggerField.wrap.getWidth();
17529                 this.dom.style.display = 'none';
17530                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17531             };
17532             t.show = function(){
17533                 var w = triggerField.wrap.getWidth();
17534                 this.dom.style.display = '';
17535                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17536             };
17537             var triggerIndex = 'Trigger'+(index+1);
17538
17539             if(this['hide'+triggerIndex]){
17540                 t.dom.style.display = 'none';
17541             }
17542             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17543             t.addClassOnOver('x-form-trigger-over');
17544             t.addClassOnClick('x-form-trigger-click');
17545         }, this);
17546         this.triggers = ts.elements;
17547     },
17548
17549     onTrigger1Click : Roo.emptyFn,
17550     onTrigger2Click : Roo.emptyFn
17551 });/*
17552  * Based on:
17553  * Ext JS Library 1.1.1
17554  * Copyright(c) 2006-2007, Ext JS, LLC.
17555  *
17556  * Originally Released Under LGPL - original licence link has changed is not relivant.
17557  *
17558  * Fork - LGPL
17559  * <script type="text/javascript">
17560  */
17561  
17562 /**
17563  * @class Roo.form.TextArea
17564  * @extends Roo.form.TextField
17565  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17566  * support for auto-sizing.
17567  * @constructor
17568  * Creates a new TextArea
17569  * @param {Object} config Configuration options
17570  */
17571 Roo.form.TextArea = function(config){
17572     Roo.form.TextArea.superclass.constructor.call(this, config);
17573     // these are provided exchanges for backwards compat
17574     // minHeight/maxHeight were replaced by growMin/growMax to be
17575     // compatible with TextField growing config values
17576     if(this.minHeight !== undefined){
17577         this.growMin = this.minHeight;
17578     }
17579     if(this.maxHeight !== undefined){
17580         this.growMax = this.maxHeight;
17581     }
17582 };
17583
17584 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17585     /**
17586      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17587      */
17588     growMin : 60,
17589     /**
17590      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17591      */
17592     growMax: 1000,
17593     /**
17594      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17595      * in the field (equivalent to setting overflow: hidden, defaults to false)
17596      */
17597     preventScrollbars: false,
17598     /**
17599      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17600      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17601      */
17602
17603     // private
17604     onRender : function(ct, position){
17605         if(!this.el){
17606             this.defaultAutoCreate = {
17607                 tag: "textarea",
17608                 style:"width:300px;height:60px;",
17609                 autocomplete: "new-password"
17610             };
17611         }
17612         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17613         if(this.grow){
17614             this.textSizeEl = Roo.DomHelper.append(document.body, {
17615                 tag: "pre", cls: "x-form-grow-sizer"
17616             });
17617             if(this.preventScrollbars){
17618                 this.el.setStyle("overflow", "hidden");
17619             }
17620             this.el.setHeight(this.growMin);
17621         }
17622     },
17623
17624     onDestroy : function(){
17625         if(this.textSizeEl){
17626             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17627         }
17628         Roo.form.TextArea.superclass.onDestroy.call(this);
17629     },
17630
17631     // private
17632     onKeyUp : function(e){
17633         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17634             this.autoSize();
17635         }
17636     },
17637
17638     /**
17639      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17640      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17641      */
17642     autoSize : function(){
17643         if(!this.grow || !this.textSizeEl){
17644             return;
17645         }
17646         var el = this.el;
17647         var v = el.dom.value;
17648         var ts = this.textSizeEl;
17649
17650         ts.innerHTML = '';
17651         ts.appendChild(document.createTextNode(v));
17652         v = ts.innerHTML;
17653
17654         Roo.fly(ts).setWidth(this.el.getWidth());
17655         if(v.length < 1){
17656             v = "&#160;&#160;";
17657         }else{
17658             if(Roo.isIE){
17659                 v = v.replace(/\n/g, '<p>&#160;</p>');
17660             }
17661             v += "&#160;\n&#160;";
17662         }
17663         ts.innerHTML = v;
17664         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17665         if(h != this.lastHeight){
17666             this.lastHeight = h;
17667             this.el.setHeight(h);
17668             this.fireEvent("autosize", this, h);
17669         }
17670     }
17671 });/*
17672  * Based on:
17673  * Ext JS Library 1.1.1
17674  * Copyright(c) 2006-2007, Ext JS, LLC.
17675  *
17676  * Originally Released Under LGPL - original licence link has changed is not relivant.
17677  *
17678  * Fork - LGPL
17679  * <script type="text/javascript">
17680  */
17681  
17682
17683 /**
17684  * @class Roo.form.NumberField
17685  * @extends Roo.form.TextField
17686  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17687  * @constructor
17688  * Creates a new NumberField
17689  * @param {Object} config Configuration options
17690  */
17691 Roo.form.NumberField = function(config){
17692     Roo.form.NumberField.superclass.constructor.call(this, config);
17693 };
17694
17695 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17696     /**
17697      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17698      */
17699     fieldClass: "x-form-field x-form-num-field",
17700     /**
17701      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17702      */
17703     allowDecimals : true,
17704     /**
17705      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17706      */
17707     decimalSeparator : ".",
17708     /**
17709      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17710      */
17711     decimalPrecision : 2,
17712     /**
17713      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17714      */
17715     allowNegative : true,
17716     /**
17717      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17718      */
17719     minValue : Number.NEGATIVE_INFINITY,
17720     /**
17721      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17722      */
17723     maxValue : Number.MAX_VALUE,
17724     /**
17725      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17726      */
17727     minText : "The minimum value for this field is {0}",
17728     /**
17729      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17730      */
17731     maxText : "The maximum value for this field is {0}",
17732     /**
17733      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17734      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17735      */
17736     nanText : "{0} is not a valid number",
17737
17738     // private
17739     initEvents : function(){
17740         Roo.form.NumberField.superclass.initEvents.call(this);
17741         var allowed = "0123456789";
17742         if(this.allowDecimals){
17743             allowed += this.decimalSeparator;
17744         }
17745         if(this.allowNegative){
17746             allowed += "-";
17747         }
17748         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17749         var keyPress = function(e){
17750             var k = e.getKey();
17751             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17752                 return;
17753             }
17754             var c = e.getCharCode();
17755             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17756                 e.stopEvent();
17757             }
17758         };
17759         this.el.on("keypress", keyPress, this);
17760     },
17761
17762     // private
17763     validateValue : function(value){
17764         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17765             return false;
17766         }
17767         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17768              return true;
17769         }
17770         var num = this.parseValue(value);
17771         if(isNaN(num)){
17772             this.markInvalid(String.format(this.nanText, value));
17773             return false;
17774         }
17775         if(num < this.minValue){
17776             this.markInvalid(String.format(this.minText, this.minValue));
17777             return false;
17778         }
17779         if(num > this.maxValue){
17780             this.markInvalid(String.format(this.maxText, this.maxValue));
17781             return false;
17782         }
17783         return true;
17784     },
17785
17786     getValue : function(){
17787         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17788     },
17789
17790     // private
17791     parseValue : function(value){
17792         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17793         return isNaN(value) ? '' : value;
17794     },
17795
17796     // private
17797     fixPrecision : function(value){
17798         var nan = isNaN(value);
17799         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17800             return nan ? '' : value;
17801         }
17802         return parseFloat(value).toFixed(this.decimalPrecision);
17803     },
17804
17805     setValue : function(v){
17806         v = this.fixPrecision(v);
17807         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17808     },
17809
17810     // private
17811     decimalPrecisionFcn : function(v){
17812         return Math.floor(v);
17813     },
17814
17815     beforeBlur : function(){
17816         var v = this.parseValue(this.getRawValue());
17817         if(v){
17818             this.setValue(v);
17819         }
17820     }
17821 });/*
17822  * Based on:
17823  * Ext JS Library 1.1.1
17824  * Copyright(c) 2006-2007, Ext JS, LLC.
17825  *
17826  * Originally Released Under LGPL - original licence link has changed is not relivant.
17827  *
17828  * Fork - LGPL
17829  * <script type="text/javascript">
17830  */
17831  
17832 /**
17833  * @class Roo.form.DateField
17834  * @extends Roo.form.TriggerField
17835  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17836 * @constructor
17837 * Create a new DateField
17838 * @param {Object} config
17839  */
17840 Roo.form.DateField = function(config){
17841     Roo.form.DateField.superclass.constructor.call(this, config);
17842     
17843       this.addEvents({
17844          
17845         /**
17846          * @event select
17847          * Fires when a date is selected
17848              * @param {Roo.form.DateField} combo This combo box
17849              * @param {Date} date The date selected
17850              */
17851         'select' : true
17852          
17853     });
17854     
17855     
17856     if(typeof this.minValue == "string") {
17857         this.minValue = this.parseDate(this.minValue);
17858     }
17859     if(typeof this.maxValue == "string") {
17860         this.maxValue = this.parseDate(this.maxValue);
17861     }
17862     this.ddMatch = null;
17863     if(this.disabledDates){
17864         var dd = this.disabledDates;
17865         var re = "(?:";
17866         for(var i = 0; i < dd.length; i++){
17867             re += dd[i];
17868             if(i != dd.length-1) {
17869                 re += "|";
17870             }
17871         }
17872         this.ddMatch = new RegExp(re + ")");
17873     }
17874 };
17875
17876 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17877     /**
17878      * @cfg {String} format
17879      * The default date format string which can be overriden for localization support.  The format must be
17880      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17881      */
17882     format : "m/d/y",
17883     /**
17884      * @cfg {String} altFormats
17885      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17886      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17887      */
17888     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17889     /**
17890      * @cfg {Array} disabledDays
17891      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17892      */
17893     disabledDays : null,
17894     /**
17895      * @cfg {String} disabledDaysText
17896      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17897      */
17898     disabledDaysText : "Disabled",
17899     /**
17900      * @cfg {Array} disabledDates
17901      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17902      * expression so they are very powerful. Some examples:
17903      * <ul>
17904      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17905      * <li>["03/08", "09/16"] would disable those days for every year</li>
17906      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17907      * <li>["03/../2006"] would disable every day in March 2006</li>
17908      * <li>["^03"] would disable every day in every March</li>
17909      * </ul>
17910      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17911      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17912      */
17913     disabledDates : null,
17914     /**
17915      * @cfg {String} disabledDatesText
17916      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17917      */
17918     disabledDatesText : "Disabled",
17919     /**
17920      * @cfg {Date/String} minValue
17921      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17922      * valid format (defaults to null).
17923      */
17924     minValue : null,
17925     /**
17926      * @cfg {Date/String} maxValue
17927      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17928      * valid format (defaults to null).
17929      */
17930     maxValue : null,
17931     /**
17932      * @cfg {String} minText
17933      * The error text to display when the date in the cell is before minValue (defaults to
17934      * 'The date in this field must be after {minValue}').
17935      */
17936     minText : "The date in this field must be equal to or after {0}",
17937     /**
17938      * @cfg {String} maxText
17939      * The error text to display when the date in the cell is after maxValue (defaults to
17940      * 'The date in this field must be before {maxValue}').
17941      */
17942     maxText : "The date in this field must be equal to or before {0}",
17943     /**
17944      * @cfg {String} invalidText
17945      * The error text to display when the date in the field is invalid (defaults to
17946      * '{value} is not a valid date - it must be in the format {format}').
17947      */
17948     invalidText : "{0} is not a valid date - it must be in the format {1}",
17949     /**
17950      * @cfg {String} triggerClass
17951      * An additional CSS class used to style the trigger button.  The trigger will always get the
17952      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17953      * which displays a calendar icon).
17954      */
17955     triggerClass : 'x-form-date-trigger',
17956     
17957
17958     /**
17959      * @cfg {Boolean} useIso
17960      * if enabled, then the date field will use a hidden field to store the 
17961      * real value as iso formated date. default (false)
17962      */ 
17963     useIso : false,
17964     /**
17965      * @cfg {String/Object} autoCreate
17966      * A DomHelper element spec, or true for a default element spec (defaults to
17967      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17968      */ 
17969     // private
17970     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17971     
17972     // private
17973     hiddenField: false,
17974     
17975     onRender : function(ct, position)
17976     {
17977         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17978         if (this.useIso) {
17979             //this.el.dom.removeAttribute('name'); 
17980             Roo.log("Changing name?");
17981             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17982             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17983                     'before', true);
17984             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17985             // prevent input submission
17986             this.hiddenName = this.name;
17987         }
17988             
17989             
17990     },
17991     
17992     // private
17993     validateValue : function(value)
17994     {
17995         value = this.formatDate(value);
17996         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17997             Roo.log('super failed');
17998             return false;
17999         }
18000         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18001              return true;
18002         }
18003         var svalue = value;
18004         value = this.parseDate(value);
18005         if(!value){
18006             Roo.log('parse date failed' + svalue);
18007             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18008             return false;
18009         }
18010         var time = value.getTime();
18011         if(this.minValue && time < this.minValue.getTime()){
18012             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18013             return false;
18014         }
18015         if(this.maxValue && time > this.maxValue.getTime()){
18016             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18017             return false;
18018         }
18019         if(this.disabledDays){
18020             var day = value.getDay();
18021             for(var i = 0; i < this.disabledDays.length; i++) {
18022                 if(day === this.disabledDays[i]){
18023                     this.markInvalid(this.disabledDaysText);
18024                     return false;
18025                 }
18026             }
18027         }
18028         var fvalue = this.formatDate(value);
18029         if(this.ddMatch && this.ddMatch.test(fvalue)){
18030             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18031             return false;
18032         }
18033         return true;
18034     },
18035
18036     // private
18037     // Provides logic to override the default TriggerField.validateBlur which just returns true
18038     validateBlur : function(){
18039         return !this.menu || !this.menu.isVisible();
18040     },
18041     
18042     getName: function()
18043     {
18044         // returns hidden if it's set..
18045         if (!this.rendered) {return ''};
18046         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
18047         
18048     },
18049
18050     /**
18051      * Returns the current date value of the date field.
18052      * @return {Date} The date value
18053      */
18054     getValue : function(){
18055         
18056         return  this.hiddenField ?
18057                 this.hiddenField.value :
18058                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
18059     },
18060
18061     /**
18062      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18063      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
18064      * (the default format used is "m/d/y").
18065      * <br />Usage:
18066      * <pre><code>
18067 //All of these calls set the same date value (May 4, 2006)
18068
18069 //Pass a date object:
18070 var dt = new Date('5/4/06');
18071 dateField.setValue(dt);
18072
18073 //Pass a date string (default format):
18074 dateField.setValue('5/4/06');
18075
18076 //Pass a date string (custom format):
18077 dateField.format = 'Y-m-d';
18078 dateField.setValue('2006-5-4');
18079 </code></pre>
18080      * @param {String/Date} date The date or valid date string
18081      */
18082     setValue : function(date){
18083         if (this.hiddenField) {
18084             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18085         }
18086         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18087         // make sure the value field is always stored as a date..
18088         this.value = this.parseDate(date);
18089         
18090         
18091     },
18092
18093     // private
18094     parseDate : function(value){
18095         if(!value || value instanceof Date){
18096             return value;
18097         }
18098         var v = Date.parseDate(value, this.format);
18099          if (!v && this.useIso) {
18100             v = Date.parseDate(value, 'Y-m-d');
18101         }
18102         if(!v && this.altFormats){
18103             if(!this.altFormatsArray){
18104                 this.altFormatsArray = this.altFormats.split("|");
18105             }
18106             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18107                 v = Date.parseDate(value, this.altFormatsArray[i]);
18108             }
18109         }
18110         return v;
18111     },
18112
18113     // private
18114     formatDate : function(date, fmt){
18115         return (!date || !(date instanceof Date)) ?
18116                date : date.dateFormat(fmt || this.format);
18117     },
18118
18119     // private
18120     menuListeners : {
18121         select: function(m, d){
18122             
18123             this.setValue(d);
18124             this.fireEvent('select', this, d);
18125         },
18126         show : function(){ // retain focus styling
18127             this.onFocus();
18128         },
18129         hide : function(){
18130             this.focus.defer(10, this);
18131             var ml = this.menuListeners;
18132             this.menu.un("select", ml.select,  this);
18133             this.menu.un("show", ml.show,  this);
18134             this.menu.un("hide", ml.hide,  this);
18135         }
18136     },
18137
18138     // private
18139     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18140     onTriggerClick : function(){
18141         if(this.disabled){
18142             return;
18143         }
18144         if(this.menu == null){
18145             this.menu = new Roo.menu.DateMenu();
18146         }
18147         Roo.apply(this.menu.picker,  {
18148             showClear: this.allowBlank,
18149             minDate : this.minValue,
18150             maxDate : this.maxValue,
18151             disabledDatesRE : this.ddMatch,
18152             disabledDatesText : this.disabledDatesText,
18153             disabledDays : this.disabledDays,
18154             disabledDaysText : this.disabledDaysText,
18155             format : this.useIso ? 'Y-m-d' : this.format,
18156             minText : String.format(this.minText, this.formatDate(this.minValue)),
18157             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18158         });
18159         this.menu.on(Roo.apply({}, this.menuListeners, {
18160             scope:this
18161         }));
18162         this.menu.picker.setValue(this.getValue() || new Date());
18163         this.menu.show(this.el, "tl-bl?");
18164     },
18165
18166     beforeBlur : function(){
18167         var v = this.parseDate(this.getRawValue());
18168         if(v){
18169             this.setValue(v);
18170         }
18171     },
18172
18173     /*@
18174      * overide
18175      * 
18176      */
18177     isDirty : function() {
18178         if(this.disabled) {
18179             return false;
18180         }
18181         
18182         if(typeof(this.startValue) === 'undefined'){
18183             return false;
18184         }
18185         
18186         return String(this.getValue()) !== String(this.startValue);
18187         
18188     }
18189 });/*
18190  * Based on:
18191  * Ext JS Library 1.1.1
18192  * Copyright(c) 2006-2007, Ext JS, LLC.
18193  *
18194  * Originally Released Under LGPL - original licence link has changed is not relivant.
18195  *
18196  * Fork - LGPL
18197  * <script type="text/javascript">
18198  */
18199  
18200 /**
18201  * @class Roo.form.MonthField
18202  * @extends Roo.form.TriggerField
18203  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18204 * @constructor
18205 * Create a new MonthField
18206 * @param {Object} config
18207  */
18208 Roo.form.MonthField = function(config){
18209     
18210     Roo.form.MonthField.superclass.constructor.call(this, config);
18211     
18212       this.addEvents({
18213          
18214         /**
18215          * @event select
18216          * Fires when a date is selected
18217              * @param {Roo.form.MonthFieeld} combo This combo box
18218              * @param {Date} date The date selected
18219              */
18220         'select' : true
18221          
18222     });
18223     
18224     
18225     if(typeof this.minValue == "string") {
18226         this.minValue = this.parseDate(this.minValue);
18227     }
18228     if(typeof this.maxValue == "string") {
18229         this.maxValue = this.parseDate(this.maxValue);
18230     }
18231     this.ddMatch = null;
18232     if(this.disabledDates){
18233         var dd = this.disabledDates;
18234         var re = "(?:";
18235         for(var i = 0; i < dd.length; i++){
18236             re += dd[i];
18237             if(i != dd.length-1) {
18238                 re += "|";
18239             }
18240         }
18241         this.ddMatch = new RegExp(re + ")");
18242     }
18243 };
18244
18245 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
18246     /**
18247      * @cfg {String} format
18248      * The default date format string which can be overriden for localization support.  The format must be
18249      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18250      */
18251     format : "M Y",
18252     /**
18253      * @cfg {String} altFormats
18254      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18255      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18256      */
18257     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18258     /**
18259      * @cfg {Array} disabledDays
18260      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18261      */
18262     disabledDays : [0,1,2,3,4,5,6],
18263     /**
18264      * @cfg {String} disabledDaysText
18265      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18266      */
18267     disabledDaysText : "Disabled",
18268     /**
18269      * @cfg {Array} disabledDates
18270      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18271      * expression so they are very powerful. Some examples:
18272      * <ul>
18273      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18274      * <li>["03/08", "09/16"] would disable those days for every year</li>
18275      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18276      * <li>["03/../2006"] would disable every day in March 2006</li>
18277      * <li>["^03"] would disable every day in every March</li>
18278      * </ul>
18279      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18280      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18281      */
18282     disabledDates : null,
18283     /**
18284      * @cfg {String} disabledDatesText
18285      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18286      */
18287     disabledDatesText : "Disabled",
18288     /**
18289      * @cfg {Date/String} minValue
18290      * The minimum allowed date. Can be either a Javascript date object or a string date in a
18291      * valid format (defaults to null).
18292      */
18293     minValue : null,
18294     /**
18295      * @cfg {Date/String} maxValue
18296      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18297      * valid format (defaults to null).
18298      */
18299     maxValue : null,
18300     /**
18301      * @cfg {String} minText
18302      * The error text to display when the date in the cell is before minValue (defaults to
18303      * 'The date in this field must be after {minValue}').
18304      */
18305     minText : "The date in this field must be equal to or after {0}",
18306     /**
18307      * @cfg {String} maxTextf
18308      * The error text to display when the date in the cell is after maxValue (defaults to
18309      * 'The date in this field must be before {maxValue}').
18310      */
18311     maxText : "The date in this field must be equal to or before {0}",
18312     /**
18313      * @cfg {String} invalidText
18314      * The error text to display when the date in the field is invalid (defaults to
18315      * '{value} is not a valid date - it must be in the format {format}').
18316      */
18317     invalidText : "{0} is not a valid date - it must be in the format {1}",
18318     /**
18319      * @cfg {String} triggerClass
18320      * An additional CSS class used to style the trigger button.  The trigger will always get the
18321      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18322      * which displays a calendar icon).
18323      */
18324     triggerClass : 'x-form-date-trigger',
18325     
18326
18327     /**
18328      * @cfg {Boolean} useIso
18329      * if enabled, then the date field will use a hidden field to store the 
18330      * real value as iso formated date. default (true)
18331      */ 
18332     useIso : true,
18333     /**
18334      * @cfg {String/Object} autoCreate
18335      * A DomHelper element spec, or true for a default element spec (defaults to
18336      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18337      */ 
18338     // private
18339     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18340     
18341     // private
18342     hiddenField: false,
18343     
18344     hideMonthPicker : false,
18345     
18346     onRender : function(ct, position)
18347     {
18348         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18349         if (this.useIso) {
18350             this.el.dom.removeAttribute('name'); 
18351             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18352                     'before', true);
18353             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18354             // prevent input submission
18355             this.hiddenName = this.name;
18356         }
18357             
18358             
18359     },
18360     
18361     // private
18362     validateValue : function(value)
18363     {
18364         value = this.formatDate(value);
18365         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18366             return false;
18367         }
18368         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18369              return true;
18370         }
18371         var svalue = value;
18372         value = this.parseDate(value);
18373         if(!value){
18374             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18375             return false;
18376         }
18377         var time = value.getTime();
18378         if(this.minValue && time < this.minValue.getTime()){
18379             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18380             return false;
18381         }
18382         if(this.maxValue && time > this.maxValue.getTime()){
18383             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18384             return false;
18385         }
18386         /*if(this.disabledDays){
18387             var day = value.getDay();
18388             for(var i = 0; i < this.disabledDays.length; i++) {
18389                 if(day === this.disabledDays[i]){
18390                     this.markInvalid(this.disabledDaysText);
18391                     return false;
18392                 }
18393             }
18394         }
18395         */
18396         var fvalue = this.formatDate(value);
18397         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18398             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18399             return false;
18400         }
18401         */
18402         return true;
18403     },
18404
18405     // private
18406     // Provides logic to override the default TriggerField.validateBlur which just returns true
18407     validateBlur : function(){
18408         return !this.menu || !this.menu.isVisible();
18409     },
18410
18411     /**
18412      * Returns the current date value of the date field.
18413      * @return {Date} The date value
18414      */
18415     getValue : function(){
18416         
18417         
18418         
18419         return  this.hiddenField ?
18420                 this.hiddenField.value :
18421                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18422     },
18423
18424     /**
18425      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18426      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18427      * (the default format used is "m/d/y").
18428      * <br />Usage:
18429      * <pre><code>
18430 //All of these calls set the same date value (May 4, 2006)
18431
18432 //Pass a date object:
18433 var dt = new Date('5/4/06');
18434 monthField.setValue(dt);
18435
18436 //Pass a date string (default format):
18437 monthField.setValue('5/4/06');
18438
18439 //Pass a date string (custom format):
18440 monthField.format = 'Y-m-d';
18441 monthField.setValue('2006-5-4');
18442 </code></pre>
18443      * @param {String/Date} date The date or valid date string
18444      */
18445     setValue : function(date){
18446         Roo.log('month setValue' + date);
18447         // can only be first of month..
18448         
18449         var val = this.parseDate(date);
18450         
18451         if (this.hiddenField) {
18452             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18453         }
18454         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18455         this.value = this.parseDate(date);
18456     },
18457
18458     // private
18459     parseDate : function(value){
18460         if(!value || value instanceof Date){
18461             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18462             return value;
18463         }
18464         var v = Date.parseDate(value, this.format);
18465         if (!v && this.useIso) {
18466             v = Date.parseDate(value, 'Y-m-d');
18467         }
18468         if (v) {
18469             // 
18470             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18471         }
18472         
18473         
18474         if(!v && this.altFormats){
18475             if(!this.altFormatsArray){
18476                 this.altFormatsArray = this.altFormats.split("|");
18477             }
18478             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18479                 v = Date.parseDate(value, this.altFormatsArray[i]);
18480             }
18481         }
18482         return v;
18483     },
18484
18485     // private
18486     formatDate : function(date, fmt){
18487         return (!date || !(date instanceof Date)) ?
18488                date : date.dateFormat(fmt || this.format);
18489     },
18490
18491     // private
18492     menuListeners : {
18493         select: function(m, d){
18494             this.setValue(d);
18495             this.fireEvent('select', this, d);
18496         },
18497         show : function(){ // retain focus styling
18498             this.onFocus();
18499         },
18500         hide : function(){
18501             this.focus.defer(10, this);
18502             var ml = this.menuListeners;
18503             this.menu.un("select", ml.select,  this);
18504             this.menu.un("show", ml.show,  this);
18505             this.menu.un("hide", ml.hide,  this);
18506         }
18507     },
18508     // private
18509     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18510     onTriggerClick : function(){
18511         if(this.disabled){
18512             return;
18513         }
18514         if(this.menu == null){
18515             this.menu = new Roo.menu.DateMenu();
18516            
18517         }
18518         
18519         Roo.apply(this.menu.picker,  {
18520             
18521             showClear: this.allowBlank,
18522             minDate : this.minValue,
18523             maxDate : this.maxValue,
18524             disabledDatesRE : this.ddMatch,
18525             disabledDatesText : this.disabledDatesText,
18526             
18527             format : this.useIso ? 'Y-m-d' : this.format,
18528             minText : String.format(this.minText, this.formatDate(this.minValue)),
18529             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18530             
18531         });
18532          this.menu.on(Roo.apply({}, this.menuListeners, {
18533             scope:this
18534         }));
18535        
18536         
18537         var m = this.menu;
18538         var p = m.picker;
18539         
18540         // hide month picker get's called when we called by 'before hide';
18541         
18542         var ignorehide = true;
18543         p.hideMonthPicker  = function(disableAnim){
18544             if (ignorehide) {
18545                 return;
18546             }
18547              if(this.monthPicker){
18548                 Roo.log("hideMonthPicker called");
18549                 if(disableAnim === true){
18550                     this.monthPicker.hide();
18551                 }else{
18552                     this.monthPicker.slideOut('t', {duration:.2});
18553                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18554                     p.fireEvent("select", this, this.value);
18555                     m.hide();
18556                 }
18557             }
18558         }
18559         
18560         Roo.log('picker set value');
18561         Roo.log(this.getValue());
18562         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18563         m.show(this.el, 'tl-bl?');
18564         ignorehide  = false;
18565         // this will trigger hideMonthPicker..
18566         
18567         
18568         // hidden the day picker
18569         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18570         
18571         
18572         
18573       
18574         
18575         p.showMonthPicker.defer(100, p);
18576     
18577         
18578        
18579     },
18580
18581     beforeBlur : function(){
18582         var v = this.parseDate(this.getRawValue());
18583         if(v){
18584             this.setValue(v);
18585         }
18586     }
18587
18588     /** @cfg {Boolean} grow @hide */
18589     /** @cfg {Number} growMin @hide */
18590     /** @cfg {Number} growMax @hide */
18591     /**
18592      * @hide
18593      * @method autoSize
18594      */
18595 });/*
18596  * Based on:
18597  * Ext JS Library 1.1.1
18598  * Copyright(c) 2006-2007, Ext JS, LLC.
18599  *
18600  * Originally Released Under LGPL - original licence link has changed is not relivant.
18601  *
18602  * Fork - LGPL
18603  * <script type="text/javascript">
18604  */
18605  
18606
18607 /**
18608  * @class Roo.form.ComboBox
18609  * @extends Roo.form.TriggerField
18610  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18611  * @constructor
18612  * Create a new ComboBox.
18613  * @param {Object} config Configuration options
18614  */
18615 Roo.form.ComboBox = function(config){
18616     Roo.form.ComboBox.superclass.constructor.call(this, config);
18617     this.addEvents({
18618         /**
18619          * @event expand
18620          * Fires when the dropdown list is expanded
18621              * @param {Roo.form.ComboBox} combo This combo box
18622              */
18623         'expand' : true,
18624         /**
18625          * @event collapse
18626          * Fires when the dropdown list is collapsed
18627              * @param {Roo.form.ComboBox} combo This combo box
18628              */
18629         'collapse' : true,
18630         /**
18631          * @event beforeselect
18632          * Fires before a list item is selected. Return false to cancel the selection.
18633              * @param {Roo.form.ComboBox} combo This combo box
18634              * @param {Roo.data.Record} record The data record returned from the underlying store
18635              * @param {Number} index The index of the selected item in the dropdown list
18636              */
18637         'beforeselect' : true,
18638         /**
18639          * @event select
18640          * Fires when a list item is selected
18641              * @param {Roo.form.ComboBox} combo This combo box
18642              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18643              * @param {Number} index The index of the selected item in the dropdown list
18644              */
18645         'select' : true,
18646         /**
18647          * @event beforequery
18648          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18649          * The event object passed has these properties:
18650              * @param {Roo.form.ComboBox} combo This combo box
18651              * @param {String} query The query
18652              * @param {Boolean} forceAll true to force "all" query
18653              * @param {Boolean} cancel true to cancel the query
18654              * @param {Object} e The query event object
18655              */
18656         'beforequery': true,
18657          /**
18658          * @event add
18659          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18660              * @param {Roo.form.ComboBox} combo This combo box
18661              */
18662         'add' : true,
18663         /**
18664          * @event edit
18665          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18666              * @param {Roo.form.ComboBox} combo This combo box
18667              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18668              */
18669         'edit' : true
18670         
18671         
18672     });
18673     if(this.transform){
18674         this.allowDomMove = false;
18675         var s = Roo.getDom(this.transform);
18676         if(!this.hiddenName){
18677             this.hiddenName = s.name;
18678         }
18679         if(!this.store){
18680             this.mode = 'local';
18681             var d = [], opts = s.options;
18682             for(var i = 0, len = opts.length;i < len; i++){
18683                 var o = opts[i];
18684                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18685                 if(o.selected) {
18686                     this.value = value;
18687                 }
18688                 d.push([value, o.text]);
18689             }
18690             this.store = new Roo.data.SimpleStore({
18691                 'id': 0,
18692                 fields: ['value', 'text'],
18693                 data : d
18694             });
18695             this.valueField = 'value';
18696             this.displayField = 'text';
18697         }
18698         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18699         if(!this.lazyRender){
18700             this.target = true;
18701             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18702             s.parentNode.removeChild(s); // remove it
18703             this.render(this.el.parentNode);
18704         }else{
18705             s.parentNode.removeChild(s); // remove it
18706         }
18707
18708     }
18709     if (this.store) {
18710         this.store = Roo.factory(this.store, Roo.data);
18711     }
18712     
18713     this.selectedIndex = -1;
18714     if(this.mode == 'local'){
18715         if(config.queryDelay === undefined){
18716             this.queryDelay = 10;
18717         }
18718         if(config.minChars === undefined){
18719             this.minChars = 0;
18720         }
18721     }
18722 };
18723
18724 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18725     /**
18726      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18727      */
18728     /**
18729      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18730      * rendering into an Roo.Editor, defaults to false)
18731      */
18732     /**
18733      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18734      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18735      */
18736     /**
18737      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18738      */
18739     /**
18740      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18741      * the dropdown list (defaults to undefined, with no header element)
18742      */
18743
18744      /**
18745      * @cfg {String/Roo.Template} tpl The template to use to render the output
18746      */
18747      
18748     // private
18749     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18750     /**
18751      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18752      */
18753     listWidth: undefined,
18754     /**
18755      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18756      * mode = 'remote' or 'text' if mode = 'local')
18757      */
18758     displayField: undefined,
18759     /**
18760      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18761      * mode = 'remote' or 'value' if mode = 'local'). 
18762      * Note: use of a valueField requires the user make a selection
18763      * in order for a value to be mapped.
18764      */
18765     valueField: undefined,
18766     
18767     
18768     /**
18769      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18770      * field's data value (defaults to the underlying DOM element's name)
18771      */
18772     hiddenName: undefined,
18773     /**
18774      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18775      */
18776     listClass: '',
18777     /**
18778      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18779      */
18780     selectedClass: 'x-combo-selected',
18781     /**
18782      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18783      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18784      * which displays a downward arrow icon).
18785      */
18786     triggerClass : 'x-form-arrow-trigger',
18787     /**
18788      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18789      */
18790     shadow:'sides',
18791     /**
18792      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18793      * anchor positions (defaults to 'tl-bl')
18794      */
18795     listAlign: 'tl-bl?',
18796     /**
18797      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18798      */
18799     maxHeight: 300,
18800     /**
18801      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18802      * query specified by the allQuery config option (defaults to 'query')
18803      */
18804     triggerAction: 'query',
18805     /**
18806      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18807      * (defaults to 4, does not apply if editable = false)
18808      */
18809     minChars : 4,
18810     /**
18811      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18812      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18813      */
18814     typeAhead: false,
18815     /**
18816      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18817      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18818      */
18819     queryDelay: 500,
18820     /**
18821      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18822      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18823      */
18824     pageSize: 0,
18825     /**
18826      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18827      * when editable = true (defaults to false)
18828      */
18829     selectOnFocus:false,
18830     /**
18831      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18832      */
18833     queryParam: 'query',
18834     /**
18835      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18836      * when mode = 'remote' (defaults to 'Loading...')
18837      */
18838     loadingText: 'Loading...',
18839     /**
18840      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18841      */
18842     resizable: false,
18843     /**
18844      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18845      */
18846     handleHeight : 8,
18847     /**
18848      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18849      * traditional select (defaults to true)
18850      */
18851     editable: true,
18852     /**
18853      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18854      */
18855     allQuery: '',
18856     /**
18857      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18858      */
18859     mode: 'remote',
18860     /**
18861      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18862      * listWidth has a higher value)
18863      */
18864     minListWidth : 70,
18865     /**
18866      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18867      * allow the user to set arbitrary text into the field (defaults to false)
18868      */
18869     forceSelection:false,
18870     /**
18871      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18872      * if typeAhead = true (defaults to 250)
18873      */
18874     typeAheadDelay : 250,
18875     /**
18876      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18877      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18878      */
18879     valueNotFoundText : undefined,
18880     /**
18881      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18882      */
18883     blockFocus : false,
18884     
18885     /**
18886      * @cfg {Boolean} disableClear Disable showing of clear button.
18887      */
18888     disableClear : false,
18889     /**
18890      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18891      */
18892     alwaysQuery : false,
18893     
18894     //private
18895     addicon : false,
18896     editicon: false,
18897     
18898     // element that contains real text value.. (when hidden is used..)
18899      
18900     // private
18901     onRender : function(ct, position){
18902         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18903         if(this.hiddenName){
18904             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18905                     'before', true);
18906             this.hiddenField.value =
18907                 this.hiddenValue !== undefined ? this.hiddenValue :
18908                 this.value !== undefined ? this.value : '';
18909
18910             // prevent input submission
18911             this.el.dom.removeAttribute('name');
18912              
18913              
18914         }
18915         if(Roo.isGecko){
18916             this.el.dom.setAttribute('autocomplete', 'off');
18917         }
18918
18919         var cls = 'x-combo-list';
18920
18921         this.list = new Roo.Layer({
18922             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18923         });
18924
18925         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18926         this.list.setWidth(lw);
18927         this.list.swallowEvent('mousewheel');
18928         this.assetHeight = 0;
18929
18930         if(this.title){
18931             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18932             this.assetHeight += this.header.getHeight();
18933         }
18934
18935         this.innerList = this.list.createChild({cls:cls+'-inner'});
18936         this.innerList.on('mouseover', this.onViewOver, this);
18937         this.innerList.on('mousemove', this.onViewMove, this);
18938         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18939         
18940         if(this.allowBlank && !this.pageSize && !this.disableClear){
18941             this.footer = this.list.createChild({cls:cls+'-ft'});
18942             this.pageTb = new Roo.Toolbar(this.footer);
18943            
18944         }
18945         if(this.pageSize){
18946             this.footer = this.list.createChild({cls:cls+'-ft'});
18947             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18948                     {pageSize: this.pageSize});
18949             
18950         }
18951         
18952         if (this.pageTb && this.allowBlank && !this.disableClear) {
18953             var _this = this;
18954             this.pageTb.add(new Roo.Toolbar.Fill(), {
18955                 cls: 'x-btn-icon x-btn-clear',
18956                 text: '&#160;',
18957                 handler: function()
18958                 {
18959                     _this.collapse();
18960                     _this.clearValue();
18961                     _this.onSelect(false, -1);
18962                 }
18963             });
18964         }
18965         if (this.footer) {
18966             this.assetHeight += this.footer.getHeight();
18967         }
18968         
18969
18970         if(!this.tpl){
18971             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18972         }
18973
18974         this.view = new Roo.View(this.innerList, this.tpl, {
18975             singleSelect:true, store: this.store, selectedClass: this.selectedClass
18976         });
18977
18978         this.view.on('click', this.onViewClick, this);
18979
18980         this.store.on('beforeload', this.onBeforeLoad, this);
18981         this.store.on('load', this.onLoad, this);
18982         this.store.on('loadexception', this.onLoadException, this);
18983
18984         if(this.resizable){
18985             this.resizer = new Roo.Resizable(this.list,  {
18986                pinned:true, handles:'se'
18987             });
18988             this.resizer.on('resize', function(r, w, h){
18989                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18990                 this.listWidth = w;
18991                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18992                 this.restrictHeight();
18993             }, this);
18994             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18995         }
18996         if(!this.editable){
18997             this.editable = true;
18998             this.setEditable(false);
18999         }  
19000         
19001         
19002         if (typeof(this.events.add.listeners) != 'undefined') {
19003             
19004             this.addicon = this.wrap.createChild(
19005                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
19006        
19007             this.addicon.on('click', function(e) {
19008                 this.fireEvent('add', this);
19009             }, this);
19010         }
19011         if (typeof(this.events.edit.listeners) != 'undefined') {
19012             
19013             this.editicon = this.wrap.createChild(
19014                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
19015             if (this.addicon) {
19016                 this.editicon.setStyle('margin-left', '40px');
19017             }
19018             this.editicon.on('click', function(e) {
19019                 
19020                 // we fire even  if inothing is selected..
19021                 this.fireEvent('edit', this, this.lastData );
19022                 
19023             }, this);
19024         }
19025         
19026         
19027         
19028     },
19029
19030     // private
19031     initEvents : function(){
19032         Roo.form.ComboBox.superclass.initEvents.call(this);
19033
19034         this.keyNav = new Roo.KeyNav(this.el, {
19035             "up" : function(e){
19036                 this.inKeyMode = true;
19037                 this.selectPrev();
19038             },
19039
19040             "down" : function(e){
19041                 if(!this.isExpanded()){
19042                     this.onTriggerClick();
19043                 }else{
19044                     this.inKeyMode = true;
19045                     this.selectNext();
19046                 }
19047             },
19048
19049             "enter" : function(e){
19050                 this.onViewClick();
19051                 //return true;
19052             },
19053
19054             "esc" : function(e){
19055                 this.collapse();
19056             },
19057
19058             "tab" : function(e){
19059                 this.onViewClick(false);
19060                 this.fireEvent("specialkey", this, e);
19061                 return true;
19062             },
19063
19064             scope : this,
19065
19066             doRelay : function(foo, bar, hname){
19067                 if(hname == 'down' || this.scope.isExpanded()){
19068                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
19069                 }
19070                 return true;
19071             },
19072
19073             forceKeyDown: true
19074         });
19075         this.queryDelay = Math.max(this.queryDelay || 10,
19076                 this.mode == 'local' ? 10 : 250);
19077         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
19078         if(this.typeAhead){
19079             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
19080         }
19081         if(this.editable !== false){
19082             this.el.on("keyup", this.onKeyUp, this);
19083         }
19084         if(this.forceSelection){
19085             this.on('blur', this.doForce, this);
19086         }
19087     },
19088
19089     onDestroy : function(){
19090         if(this.view){
19091             this.view.setStore(null);
19092             this.view.el.removeAllListeners();
19093             this.view.el.remove();
19094             this.view.purgeListeners();
19095         }
19096         if(this.list){
19097             this.list.destroy();
19098         }
19099         if(this.store){
19100             this.store.un('beforeload', this.onBeforeLoad, this);
19101             this.store.un('load', this.onLoad, this);
19102             this.store.un('loadexception', this.onLoadException, this);
19103         }
19104         Roo.form.ComboBox.superclass.onDestroy.call(this);
19105     },
19106
19107     // private
19108     fireKey : function(e){
19109         if(e.isNavKeyPress() && !this.list.isVisible()){
19110             this.fireEvent("specialkey", this, e);
19111         }
19112     },
19113
19114     // private
19115     onResize: function(w, h){
19116         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19117         
19118         if(typeof w != 'number'){
19119             // we do not handle it!?!?
19120             return;
19121         }
19122         var tw = this.trigger.getWidth();
19123         tw += this.addicon ? this.addicon.getWidth() : 0;
19124         tw += this.editicon ? this.editicon.getWidth() : 0;
19125         var x = w - tw;
19126         this.el.setWidth( this.adjustWidth('input', x));
19127             
19128         this.trigger.setStyle('left', x+'px');
19129         
19130         if(this.list && this.listWidth === undefined){
19131             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19132             this.list.setWidth(lw);
19133             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19134         }
19135         
19136     
19137         
19138     },
19139
19140     /**
19141      * Allow or prevent the user from directly editing the field text.  If false is passed,
19142      * the user will only be able to select from the items defined in the dropdown list.  This method
19143      * is the runtime equivalent of setting the 'editable' config option at config time.
19144      * @param {Boolean} value True to allow the user to directly edit the field text
19145      */
19146     setEditable : function(value){
19147         if(value == this.editable){
19148             return;
19149         }
19150         this.editable = value;
19151         if(!value){
19152             this.el.dom.setAttribute('readOnly', true);
19153             this.el.on('mousedown', this.onTriggerClick,  this);
19154             this.el.addClass('x-combo-noedit');
19155         }else{
19156             this.el.dom.setAttribute('readOnly', false);
19157             this.el.un('mousedown', this.onTriggerClick,  this);
19158             this.el.removeClass('x-combo-noedit');
19159         }
19160     },
19161
19162     // private
19163     onBeforeLoad : function(){
19164         if(!this.hasFocus){
19165             return;
19166         }
19167         this.innerList.update(this.loadingText ?
19168                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19169         this.restrictHeight();
19170         this.selectedIndex = -1;
19171     },
19172
19173     // private
19174     onLoad : function(){
19175         if(!this.hasFocus){
19176             return;
19177         }
19178         if(this.store.getCount() > 0){
19179             this.expand();
19180             this.restrictHeight();
19181             if(this.lastQuery == this.allQuery){
19182                 if(this.editable){
19183                     this.el.dom.select();
19184                 }
19185                 if(!this.selectByValue(this.value, true)){
19186                     this.select(0, true);
19187                 }
19188             }else{
19189                 this.selectNext();
19190                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19191                     this.taTask.delay(this.typeAheadDelay);
19192                 }
19193             }
19194         }else{
19195             this.onEmptyResults();
19196         }
19197         //this.el.focus();
19198     },
19199     // private
19200     onLoadException : function()
19201     {
19202         this.collapse();
19203         Roo.log(this.store.reader.jsonData);
19204         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19205             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19206         }
19207         
19208         
19209     },
19210     // private
19211     onTypeAhead : function(){
19212         if(this.store.getCount() > 0){
19213             var r = this.store.getAt(0);
19214             var newValue = r.data[this.displayField];
19215             var len = newValue.length;
19216             var selStart = this.getRawValue().length;
19217             if(selStart != len){
19218                 this.setRawValue(newValue);
19219                 this.selectText(selStart, newValue.length);
19220             }
19221         }
19222     },
19223
19224     // private
19225     onSelect : function(record, index){
19226         if(this.fireEvent('beforeselect', this, record, index) !== false){
19227             this.setFromData(index > -1 ? record.data : false);
19228             this.collapse();
19229             this.fireEvent('select', this, record, index);
19230         }
19231     },
19232
19233     /**
19234      * Returns the currently selected field value or empty string if no value is set.
19235      * @return {String} value The selected value
19236      */
19237     getValue : function(){
19238         if(this.valueField){
19239             return typeof this.value != 'undefined' ? this.value : '';
19240         }
19241         return Roo.form.ComboBox.superclass.getValue.call(this);
19242     },
19243
19244     /**
19245      * Clears any text/value currently set in the field
19246      */
19247     clearValue : function(){
19248         if(this.hiddenField){
19249             this.hiddenField.value = '';
19250         }
19251         this.value = '';
19252         this.setRawValue('');
19253         this.lastSelectionText = '';
19254         
19255     },
19256
19257     /**
19258      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
19259      * will be displayed in the field.  If the value does not match the data value of an existing item,
19260      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19261      * Otherwise the field will be blank (although the value will still be set).
19262      * @param {String} value The value to match
19263      */
19264     setValue : function(v){
19265         var text = v;
19266         if(this.valueField){
19267             var r = this.findRecord(this.valueField, v);
19268             if(r){
19269                 text = r.data[this.displayField];
19270             }else if(this.valueNotFoundText !== undefined){
19271                 text = this.valueNotFoundText;
19272             }
19273         }
19274         this.lastSelectionText = text;
19275         if(this.hiddenField){
19276             this.hiddenField.value = v;
19277         }
19278         Roo.form.ComboBox.superclass.setValue.call(this, text);
19279         this.value = v;
19280     },
19281     /**
19282      * @property {Object} the last set data for the element
19283      */
19284     
19285     lastData : false,
19286     /**
19287      * Sets the value of the field based on a object which is related to the record format for the store.
19288      * @param {Object} value the value to set as. or false on reset?
19289      */
19290     setFromData : function(o){
19291         var dv = ''; // display value
19292         var vv = ''; // value value..
19293         this.lastData = o;
19294         if (this.displayField) {
19295             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19296         } else {
19297             // this is an error condition!!!
19298             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19299         }
19300         
19301         if(this.valueField){
19302             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19303         }
19304         if(this.hiddenField){
19305             this.hiddenField.value = vv;
19306             
19307             this.lastSelectionText = dv;
19308             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19309             this.value = vv;
19310             return;
19311         }
19312         // no hidden field.. - we store the value in 'value', but still display
19313         // display field!!!!
19314         this.lastSelectionText = dv;
19315         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19316         this.value = vv;
19317         
19318         
19319     },
19320     // private
19321     reset : function(){
19322         // overridden so that last data is reset..
19323         this.setValue(this.resetValue);
19324         this.originalValue = this.getValue();
19325         this.clearInvalid();
19326         this.lastData = false;
19327         if (this.view) {
19328             this.view.clearSelections();
19329         }
19330     },
19331     // private
19332     findRecord : function(prop, value){
19333         var record;
19334         if(this.store.getCount() > 0){
19335             this.store.each(function(r){
19336                 if(r.data[prop] == value){
19337                     record = r;
19338                     return false;
19339                 }
19340                 return true;
19341             });
19342         }
19343         return record;
19344     },
19345     
19346     getName: function()
19347     {
19348         // returns hidden if it's set..
19349         if (!this.rendered) {return ''};
19350         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19351         
19352     },
19353     // private
19354     onViewMove : function(e, t){
19355         this.inKeyMode = false;
19356     },
19357
19358     // private
19359     onViewOver : function(e, t){
19360         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19361             return;
19362         }
19363         var item = this.view.findItemFromChild(t);
19364         if(item){
19365             var index = this.view.indexOf(item);
19366             this.select(index, false);
19367         }
19368     },
19369
19370     // private
19371     onViewClick : function(doFocus)
19372     {
19373         var index = this.view.getSelectedIndexes()[0];
19374         var r = this.store.getAt(index);
19375         if(r){
19376             this.onSelect(r, index);
19377         }
19378         if(doFocus !== false && !this.blockFocus){
19379             this.el.focus();
19380         }
19381     },
19382
19383     // private
19384     restrictHeight : function(){
19385         this.innerList.dom.style.height = '';
19386         var inner = this.innerList.dom;
19387         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19388         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19389         this.list.beginUpdate();
19390         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19391         this.list.alignTo(this.el, this.listAlign);
19392         this.list.endUpdate();
19393     },
19394
19395     // private
19396     onEmptyResults : function(){
19397         this.collapse();
19398     },
19399
19400     /**
19401      * Returns true if the dropdown list is expanded, else false.
19402      */
19403     isExpanded : function(){
19404         return this.list.isVisible();
19405     },
19406
19407     /**
19408      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19409      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19410      * @param {String} value The data value of the item to select
19411      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19412      * selected item if it is not currently in view (defaults to true)
19413      * @return {Boolean} True if the value matched an item in the list, else false
19414      */
19415     selectByValue : function(v, scrollIntoView){
19416         if(v !== undefined && v !== null){
19417             var r = this.findRecord(this.valueField || this.displayField, v);
19418             if(r){
19419                 this.select(this.store.indexOf(r), scrollIntoView);
19420                 return true;
19421             }
19422         }
19423         return false;
19424     },
19425
19426     /**
19427      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19428      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19429      * @param {Number} index The zero-based index of the list item to select
19430      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19431      * selected item if it is not currently in view (defaults to true)
19432      */
19433     select : function(index, scrollIntoView){
19434         this.selectedIndex = index;
19435         this.view.select(index);
19436         if(scrollIntoView !== false){
19437             var el = this.view.getNode(index);
19438             if(el){
19439                 this.innerList.scrollChildIntoView(el, false);
19440             }
19441         }
19442     },
19443
19444     // private
19445     selectNext : function(){
19446         var ct = this.store.getCount();
19447         if(ct > 0){
19448             if(this.selectedIndex == -1){
19449                 this.select(0);
19450             }else if(this.selectedIndex < ct-1){
19451                 this.select(this.selectedIndex+1);
19452             }
19453         }
19454     },
19455
19456     // private
19457     selectPrev : function(){
19458         var ct = this.store.getCount();
19459         if(ct > 0){
19460             if(this.selectedIndex == -1){
19461                 this.select(0);
19462             }else if(this.selectedIndex != 0){
19463                 this.select(this.selectedIndex-1);
19464             }
19465         }
19466     },
19467
19468     // private
19469     onKeyUp : function(e){
19470         if(this.editable !== false && !e.isSpecialKey()){
19471             this.lastKey = e.getKey();
19472             this.dqTask.delay(this.queryDelay);
19473         }
19474     },
19475
19476     // private
19477     validateBlur : function(){
19478         return !this.list || !this.list.isVisible();   
19479     },
19480
19481     // private
19482     initQuery : function(){
19483         this.doQuery(this.getRawValue());
19484     },
19485
19486     // private
19487     doForce : function(){
19488         if(this.el.dom.value.length > 0){
19489             this.el.dom.value =
19490                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19491              
19492         }
19493     },
19494
19495     /**
19496      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19497      * query allowing the query action to be canceled if needed.
19498      * @param {String} query The SQL query to execute
19499      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19500      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19501      * saved in the current store (defaults to false)
19502      */
19503     doQuery : function(q, forceAll){
19504         if(q === undefined || q === null){
19505             q = '';
19506         }
19507         var qe = {
19508             query: q,
19509             forceAll: forceAll,
19510             combo: this,
19511             cancel:false
19512         };
19513         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19514             return false;
19515         }
19516         q = qe.query;
19517         forceAll = qe.forceAll;
19518         if(forceAll === true || (q.length >= this.minChars)){
19519             if(this.lastQuery != q || this.alwaysQuery){
19520                 this.lastQuery = q;
19521                 if(this.mode == 'local'){
19522                     this.selectedIndex = -1;
19523                     if(forceAll){
19524                         this.store.clearFilter();
19525                     }else{
19526                         this.store.filter(this.displayField, q);
19527                     }
19528                     this.onLoad();
19529                 }else{
19530                     this.store.baseParams[this.queryParam] = q;
19531                     this.store.load({
19532                         params: this.getParams(q)
19533                     });
19534                     this.expand();
19535                 }
19536             }else{
19537                 this.selectedIndex = -1;
19538                 this.onLoad();   
19539             }
19540         }
19541     },
19542
19543     // private
19544     getParams : function(q){
19545         var p = {};
19546         //p[this.queryParam] = q;
19547         if(this.pageSize){
19548             p.start = 0;
19549             p.limit = this.pageSize;
19550         }
19551         return p;
19552     },
19553
19554     /**
19555      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19556      */
19557     collapse : function(){
19558         if(!this.isExpanded()){
19559             return;
19560         }
19561         this.list.hide();
19562         Roo.get(document).un('mousedown', this.collapseIf, this);
19563         Roo.get(document).un('mousewheel', this.collapseIf, this);
19564         if (!this.editable) {
19565             Roo.get(document).un('keydown', this.listKeyPress, this);
19566         }
19567         this.fireEvent('collapse', this);
19568     },
19569
19570     // private
19571     collapseIf : function(e){
19572         if(!e.within(this.wrap) && !e.within(this.list)){
19573             this.collapse();
19574         }
19575     },
19576
19577     /**
19578      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19579      */
19580     expand : function(){
19581         if(this.isExpanded() || !this.hasFocus){
19582             return;
19583         }
19584         this.list.alignTo(this.el, this.listAlign);
19585         this.list.show();
19586         Roo.get(document).on('mousedown', this.collapseIf, this);
19587         Roo.get(document).on('mousewheel', this.collapseIf, this);
19588         if (!this.editable) {
19589             Roo.get(document).on('keydown', this.listKeyPress, this);
19590         }
19591         
19592         this.fireEvent('expand', this);
19593     },
19594
19595     // private
19596     // Implements the default empty TriggerField.onTriggerClick function
19597     onTriggerClick : function(){
19598         if(this.disabled){
19599             return;
19600         }
19601         if(this.isExpanded()){
19602             this.collapse();
19603             if (!this.blockFocus) {
19604                 this.el.focus();
19605             }
19606             
19607         }else {
19608             this.hasFocus = true;
19609             if(this.triggerAction == 'all') {
19610                 this.doQuery(this.allQuery, true);
19611             } else {
19612                 this.doQuery(this.getRawValue());
19613             }
19614             if (!this.blockFocus) {
19615                 this.el.focus();
19616             }
19617         }
19618     },
19619     listKeyPress : function(e)
19620     {
19621         //Roo.log('listkeypress');
19622         // scroll to first matching element based on key pres..
19623         if (e.isSpecialKey()) {
19624             return false;
19625         }
19626         var k = String.fromCharCode(e.getKey()).toUpperCase();
19627         //Roo.log(k);
19628         var match  = false;
19629         var csel = this.view.getSelectedNodes();
19630         var cselitem = false;
19631         if (csel.length) {
19632             var ix = this.view.indexOf(csel[0]);
19633             cselitem  = this.store.getAt(ix);
19634             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19635                 cselitem = false;
19636             }
19637             
19638         }
19639         
19640         this.store.each(function(v) { 
19641             if (cselitem) {
19642                 // start at existing selection.
19643                 if (cselitem.id == v.id) {
19644                     cselitem = false;
19645                 }
19646                 return;
19647             }
19648                 
19649             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19650                 match = this.store.indexOf(v);
19651                 return false;
19652             }
19653         }, this);
19654         
19655         if (match === false) {
19656             return true; // no more action?
19657         }
19658         // scroll to?
19659         this.view.select(match);
19660         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19661         sn.scrollIntoView(sn.dom.parentNode, false);
19662     }
19663
19664     /** 
19665     * @cfg {Boolean} grow 
19666     * @hide 
19667     */
19668     /** 
19669     * @cfg {Number} growMin 
19670     * @hide 
19671     */
19672     /** 
19673     * @cfg {Number} growMax 
19674     * @hide 
19675     */
19676     /**
19677      * @hide
19678      * @method autoSize
19679      */
19680 });/*
19681  * Copyright(c) 2010-2012, Roo J Solutions Limited
19682  *
19683  * Licence LGPL
19684  *
19685  */
19686
19687 /**
19688  * @class Roo.form.ComboBoxArray
19689  * @extends Roo.form.TextField
19690  * A facebook style adder... for lists of email / people / countries  etc...
19691  * pick multiple items from a combo box, and shows each one.
19692  *
19693  *  Fred [x]  Brian [x]  [Pick another |v]
19694  *
19695  *
19696  *  For this to work: it needs various extra information
19697  *    - normal combo problay has
19698  *      name, hiddenName
19699  *    + displayField, valueField
19700  *
19701  *    For our purpose...
19702  *
19703  *
19704  *   If we change from 'extends' to wrapping...
19705  *   
19706  *  
19707  *
19708  
19709  
19710  * @constructor
19711  * Create a new ComboBoxArray.
19712  * @param {Object} config Configuration options
19713  */
19714  
19715
19716 Roo.form.ComboBoxArray = function(config)
19717 {
19718     this.addEvents({
19719         /**
19720          * @event beforeremove
19721          * Fires before remove the value from the list
19722              * @param {Roo.form.ComboBoxArray} _self This combo box array
19723              * @param {Roo.form.ComboBoxArray.Item} item removed item
19724              */
19725         'beforeremove' : true,
19726         /**
19727          * @event remove
19728          * Fires when remove the value from the list
19729              * @param {Roo.form.ComboBoxArray} _self This combo box array
19730              * @param {Roo.form.ComboBoxArray.Item} item removed item
19731              */
19732         'remove' : true
19733         
19734         
19735     });
19736     
19737     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19738     
19739     this.items = new Roo.util.MixedCollection(false);
19740     
19741     // construct the child combo...
19742     
19743     
19744     
19745     
19746    
19747     
19748 }
19749
19750  
19751 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19752
19753     /**
19754      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19755      */
19756     
19757     lastData : false,
19758     
19759     // behavies liek a hiddne field
19760     inputType:      'hidden',
19761     /**
19762      * @cfg {Number} width The width of the box that displays the selected element
19763      */ 
19764     width:          300,
19765
19766     
19767     
19768     /**
19769      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19770      */
19771     name : false,
19772     /**
19773      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19774      */
19775     hiddenName : false,
19776     
19777     
19778     // private the array of items that are displayed..
19779     items  : false,
19780     // private - the hidden field el.
19781     hiddenEl : false,
19782     // private - the filed el..
19783     el : false,
19784     
19785     //validateValue : function() { return true; }, // all values are ok!
19786     //onAddClick: function() { },
19787     
19788     onRender : function(ct, position) 
19789     {
19790         
19791         // create the standard hidden element
19792         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19793         
19794         
19795         // give fake names to child combo;
19796         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19797         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
19798         
19799         this.combo = Roo.factory(this.combo, Roo.form);
19800         this.combo.onRender(ct, position);
19801         if (typeof(this.combo.width) != 'undefined') {
19802             this.combo.onResize(this.combo.width,0);
19803         }
19804         
19805         this.combo.initEvents();
19806         
19807         // assigned so form know we need to do this..
19808         this.store          = this.combo.store;
19809         this.valueField     = this.combo.valueField;
19810         this.displayField   = this.combo.displayField ;
19811         
19812         
19813         this.combo.wrap.addClass('x-cbarray-grp');
19814         
19815         var cbwrap = this.combo.wrap.createChild(
19816             {tag: 'div', cls: 'x-cbarray-cb'},
19817             this.combo.el.dom
19818         );
19819         
19820              
19821         this.hiddenEl = this.combo.wrap.createChild({
19822             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19823         });
19824         this.el = this.combo.wrap.createChild({
19825             tag: 'input',  type:'hidden' , name: this.name, value : ''
19826         });
19827          //   this.el.dom.removeAttribute("name");
19828         
19829         
19830         this.outerWrap = this.combo.wrap;
19831         this.wrap = cbwrap;
19832         
19833         this.outerWrap.setWidth(this.width);
19834         this.outerWrap.dom.removeChild(this.el.dom);
19835         
19836         this.wrap.dom.appendChild(this.el.dom);
19837         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19838         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19839         
19840         this.combo.trigger.setStyle('position','relative');
19841         this.combo.trigger.setStyle('left', '0px');
19842         this.combo.trigger.setStyle('top', '2px');
19843         
19844         this.combo.el.setStyle('vertical-align', 'text-bottom');
19845         
19846         //this.trigger.setStyle('vertical-align', 'top');
19847         
19848         // this should use the code from combo really... on('add' ....)
19849         if (this.adder) {
19850             
19851         
19852             this.adder = this.outerWrap.createChild(
19853                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19854             var _t = this;
19855             this.adder.on('click', function(e) {
19856                 _t.fireEvent('adderclick', this, e);
19857             }, _t);
19858         }
19859         //var _t = this;
19860         //this.adder.on('click', this.onAddClick, _t);
19861         
19862         
19863         this.combo.on('select', function(cb, rec, ix) {
19864             this.addItem(rec.data);
19865             
19866             cb.setValue('');
19867             cb.el.dom.value = '';
19868             //cb.lastData = rec.data;
19869             // add to list
19870             
19871         }, this);
19872         
19873         
19874     },
19875     
19876     
19877     getName: function()
19878     {
19879         // returns hidden if it's set..
19880         if (!this.rendered) {return ''};
19881         return  this.hiddenName ? this.hiddenName : this.name;
19882         
19883     },
19884     
19885     
19886     onResize: function(w, h){
19887         
19888         return;
19889         // not sure if this is needed..
19890         //this.combo.onResize(w,h);
19891         
19892         if(typeof w != 'number'){
19893             // we do not handle it!?!?
19894             return;
19895         }
19896         var tw = this.combo.trigger.getWidth();
19897         tw += this.addicon ? this.addicon.getWidth() : 0;
19898         tw += this.editicon ? this.editicon.getWidth() : 0;
19899         var x = w - tw;
19900         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19901             
19902         this.combo.trigger.setStyle('left', '0px');
19903         
19904         if(this.list && this.listWidth === undefined){
19905             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19906             this.list.setWidth(lw);
19907             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19908         }
19909         
19910     
19911         
19912     },
19913     
19914     addItem: function(rec)
19915     {
19916         var valueField = this.combo.valueField;
19917         var displayField = this.combo.displayField;
19918         if (this.items.indexOfKey(rec[valueField]) > -1) {
19919             //console.log("GOT " + rec.data.id);
19920             return;
19921         }
19922         
19923         var x = new Roo.form.ComboBoxArray.Item({
19924             //id : rec[this.idField],
19925             data : rec,
19926             displayField : displayField ,
19927             tipField : displayField ,
19928             cb : this
19929         });
19930         // use the 
19931         this.items.add(rec[valueField],x);
19932         // add it before the element..
19933         this.updateHiddenEl();
19934         x.render(this.outerWrap, this.wrap.dom);
19935         // add the image handler..
19936     },
19937     
19938     updateHiddenEl : function()
19939     {
19940         this.validate();
19941         if (!this.hiddenEl) {
19942             return;
19943         }
19944         var ar = [];
19945         var idField = this.combo.valueField;
19946         
19947         this.items.each(function(f) {
19948             ar.push(f.data[idField]);
19949            
19950         });
19951         this.hiddenEl.dom.value = ar.join(',');
19952         this.validate();
19953     },
19954     
19955     reset : function()
19956     {
19957         this.items.clear();
19958         
19959         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19960            el.remove();
19961         });
19962         
19963         this.el.dom.value = '';
19964         if (this.hiddenEl) {
19965             this.hiddenEl.dom.value = '';
19966         }
19967         
19968     },
19969     getValue: function()
19970     {
19971         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19972     },
19973     setValue: function(v) // not a valid action - must use addItems..
19974     {
19975          
19976         this.reset();
19977         
19978         
19979         
19980         if (this.store.isLocal && (typeof(v) == 'string')) {
19981             // then we can use the store to find the values..
19982             // comma seperated at present.. this needs to allow JSON based encoding..
19983             this.hiddenEl.value  = v;
19984             var v_ar = [];
19985             Roo.each(v.split(','), function(k) {
19986                 Roo.log("CHECK " + this.valueField + ',' + k);
19987                 var li = this.store.query(this.valueField, k);
19988                 if (!li.length) {
19989                     return;
19990                 }
19991                 var add = {};
19992                 add[this.valueField] = k;
19993                 add[this.displayField] = li.item(0).data[this.displayField];
19994                 
19995                 this.addItem(add);
19996             }, this) 
19997              
19998         }
19999         if (typeof(v) == 'object' ) {
20000             // then let's assume it's an array of objects..
20001             Roo.each(v, function(l) {
20002                 this.addItem(l);
20003             }, this);
20004              
20005         }
20006         
20007         
20008     },
20009     setFromData: function(v)
20010     {
20011         // this recieves an object, if setValues is called.
20012         this.reset();
20013         this.el.dom.value = v[this.displayField];
20014         this.hiddenEl.dom.value = v[this.valueField];
20015         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
20016             return;
20017         }
20018         var kv = v[this.valueField];
20019         var dv = v[this.displayField];
20020         kv = typeof(kv) != 'string' ? '' : kv;
20021         dv = typeof(dv) != 'string' ? '' : dv;
20022         
20023         
20024         var keys = kv.split(',');
20025         var display = dv.split(',');
20026         for (var i = 0 ; i < keys.length; i++) {
20027             
20028             add = {};
20029             add[this.valueField] = keys[i];
20030             add[this.displayField] = display[i];
20031             this.addItem(add);
20032         }
20033       
20034         
20035     },
20036     
20037     /**
20038      * Validates the combox array value
20039      * @return {Boolean} True if the value is valid, else false
20040      */
20041     validate : function(){
20042         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
20043             this.clearInvalid();
20044             return true;
20045         }
20046         return false;
20047     },
20048     
20049     validateValue : function(value){
20050         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
20051         
20052     },
20053     
20054     /*@
20055      * overide
20056      * 
20057      */
20058     isDirty : function() {
20059         if(this.disabled) {
20060             return false;
20061         }
20062         
20063         try {
20064             var d = Roo.decode(String(this.originalValue));
20065         } catch (e) {
20066             return String(this.getValue()) !== String(this.originalValue);
20067         }
20068         
20069         var originalValue = [];
20070         
20071         for (var i = 0; i < d.length; i++){
20072             originalValue.push(d[i][this.valueField]);
20073         }
20074         
20075         return String(this.getValue()) !== String(originalValue.join(','));
20076         
20077     }
20078     
20079 });
20080
20081
20082
20083 /**
20084  * @class Roo.form.ComboBoxArray.Item
20085  * @extends Roo.BoxComponent
20086  * A selected item in the list
20087  *  Fred [x]  Brian [x]  [Pick another |v]
20088  * 
20089  * @constructor
20090  * Create a new item.
20091  * @param {Object} config Configuration options
20092  */
20093  
20094 Roo.form.ComboBoxArray.Item = function(config) {
20095     config.id = Roo.id();
20096     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
20097 }
20098
20099 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
20100     data : {},
20101     cb: false,
20102     displayField : false,
20103     tipField : false,
20104     
20105     
20106     defaultAutoCreate : {
20107         tag: 'div',
20108         cls: 'x-cbarray-item',
20109         cn : [ 
20110             { tag: 'div' },
20111             {
20112                 tag: 'img',
20113                 width:16,
20114                 height : 16,
20115                 src : Roo.BLANK_IMAGE_URL ,
20116                 align: 'center'
20117             }
20118         ]
20119         
20120     },
20121     
20122  
20123     onRender : function(ct, position)
20124     {
20125         Roo.form.Field.superclass.onRender.call(this, ct, position);
20126         
20127         if(!this.el){
20128             var cfg = this.getAutoCreate();
20129             this.el = ct.createChild(cfg, position);
20130         }
20131         
20132         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20133         
20134         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
20135             this.cb.renderer(this.data) :
20136             String.format('{0}',this.data[this.displayField]);
20137         
20138             
20139         this.el.child('div').dom.setAttribute('qtip',
20140                         String.format('{0}',this.data[this.tipField])
20141         );
20142         
20143         this.el.child('img').on('click', this.remove, this);
20144         
20145     },
20146    
20147     remove : function()
20148     {
20149         if(this.cb.disabled){
20150             return;
20151         }
20152         
20153         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20154             this.cb.items.remove(this);
20155             this.el.child('img').un('click', this.remove, this);
20156             this.el.remove();
20157             this.cb.updateHiddenEl();
20158
20159             this.cb.fireEvent('remove', this.cb, this);
20160         }
20161         
20162     }
20163 });/*
20164  * Based on:
20165  * Ext JS Library 1.1.1
20166  * Copyright(c) 2006-2007, Ext JS, LLC.
20167  *
20168  * Originally Released Under LGPL - original licence link has changed is not relivant.
20169  *
20170  * Fork - LGPL
20171  * <script type="text/javascript">
20172  */
20173 /**
20174  * @class Roo.form.Checkbox
20175  * @extends Roo.form.Field
20176  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20177  * @constructor
20178  * Creates a new Checkbox
20179  * @param {Object} config Configuration options
20180  */
20181 Roo.form.Checkbox = function(config){
20182     Roo.form.Checkbox.superclass.constructor.call(this, config);
20183     this.addEvents({
20184         /**
20185          * @event check
20186          * Fires when the checkbox is checked or unchecked.
20187              * @param {Roo.form.Checkbox} this This checkbox
20188              * @param {Boolean} checked The new checked value
20189              */
20190         check : true
20191     });
20192 };
20193
20194 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20195     /**
20196      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20197      */
20198     focusClass : undefined,
20199     /**
20200      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20201      */
20202     fieldClass: "x-form-field",
20203     /**
20204      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20205      */
20206     checked: false,
20207     /**
20208      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20209      * {tag: "input", type: "checkbox", autocomplete: "off"})
20210      */
20211     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20212     /**
20213      * @cfg {String} boxLabel The text that appears beside the checkbox
20214      */
20215     boxLabel : "",
20216     /**
20217      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20218      */  
20219     inputValue : '1',
20220     /**
20221      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20222      */
20223      valueOff: '0', // value when not checked..
20224
20225     actionMode : 'viewEl', 
20226     //
20227     // private
20228     itemCls : 'x-menu-check-item x-form-item',
20229     groupClass : 'x-menu-group-item',
20230     inputType : 'hidden',
20231     
20232     
20233     inSetChecked: false, // check that we are not calling self...
20234     
20235     inputElement: false, // real input element?
20236     basedOn: false, // ????
20237     
20238     isFormField: true, // not sure where this is needed!!!!
20239
20240     onResize : function(){
20241         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20242         if(!this.boxLabel){
20243             this.el.alignTo(this.wrap, 'c-c');
20244         }
20245     },
20246
20247     initEvents : function(){
20248         Roo.form.Checkbox.superclass.initEvents.call(this);
20249         this.el.on("click", this.onClick,  this);
20250         this.el.on("change", this.onClick,  this);
20251     },
20252
20253
20254     getResizeEl : function(){
20255         return this.wrap;
20256     },
20257
20258     getPositionEl : function(){
20259         return this.wrap;
20260     },
20261
20262     // private
20263     onRender : function(ct, position){
20264         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20265         /*
20266         if(this.inputValue !== undefined){
20267             this.el.dom.value = this.inputValue;
20268         }
20269         */
20270         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20271         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20272         var viewEl = this.wrap.createChild({ 
20273             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20274         this.viewEl = viewEl;   
20275         this.wrap.on('click', this.onClick,  this); 
20276         
20277         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20278         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20279         
20280         
20281         
20282         if(this.boxLabel){
20283             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20284         //    viewEl.on('click', this.onClick,  this); 
20285         }
20286         //if(this.checked){
20287             this.setChecked(this.checked);
20288         //}else{
20289             //this.checked = this.el.dom;
20290         //}
20291
20292     },
20293
20294     // private
20295     initValue : Roo.emptyFn,
20296
20297     /**
20298      * Returns the checked state of the checkbox.
20299      * @return {Boolean} True if checked, else false
20300      */
20301     getValue : function(){
20302         if(this.el){
20303             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20304         }
20305         return this.valueOff;
20306         
20307     },
20308
20309         // private
20310     onClick : function(){ 
20311         if (this.disabled) {
20312             return;
20313         }
20314         this.setChecked(!this.checked);
20315
20316         //if(this.el.dom.checked != this.checked){
20317         //    this.setValue(this.el.dom.checked);
20318        // }
20319     },
20320
20321     /**
20322      * Sets the checked state of the checkbox.
20323      * On is always based on a string comparison between inputValue and the param.
20324      * @param {Boolean/String} value - the value to set 
20325      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20326      */
20327     setValue : function(v,suppressEvent){
20328         
20329         
20330         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20331         //if(this.el && this.el.dom){
20332         //    this.el.dom.checked = this.checked;
20333         //    this.el.dom.defaultChecked = this.checked;
20334         //}
20335         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20336         //this.fireEvent("check", this, this.checked);
20337     },
20338     // private..
20339     setChecked : function(state,suppressEvent)
20340     {
20341         if (this.inSetChecked) {
20342             this.checked = state;
20343             return;
20344         }
20345         
20346     
20347         if(this.wrap){
20348             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20349         }
20350         this.checked = state;
20351         if(suppressEvent !== true){
20352             this.fireEvent('check', this, state);
20353         }
20354         this.inSetChecked = true;
20355         this.el.dom.value = state ? this.inputValue : this.valueOff;
20356         this.inSetChecked = false;
20357         
20358     },
20359     // handle setting of hidden value by some other method!!?!?
20360     setFromHidden: function()
20361     {
20362         if(!this.el){
20363             return;
20364         }
20365         //console.log("SET FROM HIDDEN");
20366         //alert('setFrom hidden');
20367         this.setValue(this.el.dom.value);
20368     },
20369     
20370     onDestroy : function()
20371     {
20372         if(this.viewEl){
20373             Roo.get(this.viewEl).remove();
20374         }
20375          
20376         Roo.form.Checkbox.superclass.onDestroy.call(this);
20377     },
20378     
20379     setBoxLabel : function(str)
20380     {
20381         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20382     }
20383
20384 });/*
20385  * Based on:
20386  * Ext JS Library 1.1.1
20387  * Copyright(c) 2006-2007, Ext JS, LLC.
20388  *
20389  * Originally Released Under LGPL - original licence link has changed is not relivant.
20390  *
20391  * Fork - LGPL
20392  * <script type="text/javascript">
20393  */
20394  
20395 /**
20396  * @class Roo.form.Radio
20397  * @extends Roo.form.Checkbox
20398  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20399  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20400  * @constructor
20401  * Creates a new Radio
20402  * @param {Object} config Configuration options
20403  */
20404 Roo.form.Radio = function(){
20405     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20406 };
20407 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20408     inputType: 'radio',
20409
20410     /**
20411      * If this radio is part of a group, it will return the selected value
20412      * @return {String}
20413      */
20414     getGroupValue : function(){
20415         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20416     },
20417     
20418     
20419     onRender : function(ct, position){
20420         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20421         
20422         if(this.inputValue !== undefined){
20423             this.el.dom.value = this.inputValue;
20424         }
20425          
20426         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20427         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20428         //var viewEl = this.wrap.createChild({ 
20429         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20430         //this.viewEl = viewEl;   
20431         //this.wrap.on('click', this.onClick,  this); 
20432         
20433         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20434         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20435         
20436         
20437         
20438         if(this.boxLabel){
20439             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20440         //    viewEl.on('click', this.onClick,  this); 
20441         }
20442          if(this.checked){
20443             this.el.dom.checked =   'checked' ;
20444         }
20445          
20446     } 
20447     
20448     
20449 });//<script type="text/javascript">
20450
20451 /*
20452  * Based  Ext JS Library 1.1.1
20453  * Copyright(c) 2006-2007, Ext JS, LLC.
20454  * LGPL
20455  *
20456  */
20457  
20458 /**
20459  * @class Roo.HtmlEditorCore
20460  * @extends Roo.Component
20461  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20462  *
20463  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20464  */
20465
20466 Roo.HtmlEditorCore = function(config){
20467     
20468     
20469     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20470     
20471     
20472     this.addEvents({
20473         /**
20474          * @event initialize
20475          * Fires when the editor is fully initialized (including the iframe)
20476          * @param {Roo.HtmlEditorCore} this
20477          */
20478         initialize: true,
20479         /**
20480          * @event activate
20481          * Fires when the editor is first receives the focus. Any insertion must wait
20482          * until after this event.
20483          * @param {Roo.HtmlEditorCore} this
20484          */
20485         activate: true,
20486          /**
20487          * @event beforesync
20488          * Fires before the textarea is updated with content from the editor iframe. Return false
20489          * to cancel the sync.
20490          * @param {Roo.HtmlEditorCore} this
20491          * @param {String} html
20492          */
20493         beforesync: true,
20494          /**
20495          * @event beforepush
20496          * Fires before the iframe editor is updated with content from the textarea. Return false
20497          * to cancel the push.
20498          * @param {Roo.HtmlEditorCore} this
20499          * @param {String} html
20500          */
20501         beforepush: true,
20502          /**
20503          * @event sync
20504          * Fires when the textarea is updated with content from the editor iframe.
20505          * @param {Roo.HtmlEditorCore} this
20506          * @param {String} html
20507          */
20508         sync: true,
20509          /**
20510          * @event push
20511          * Fires when the iframe editor is updated with content from the textarea.
20512          * @param {Roo.HtmlEditorCore} this
20513          * @param {String} html
20514          */
20515         push: true,
20516         
20517         /**
20518          * @event editorevent
20519          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20520          * @param {Roo.HtmlEditorCore} this
20521          */
20522         editorevent: true
20523         
20524     });
20525     
20526     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20527     
20528     // defaults : white / black...
20529     this.applyBlacklists();
20530     
20531     
20532     
20533 };
20534
20535
20536 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20537
20538
20539      /**
20540      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20541      */
20542     
20543     owner : false,
20544     
20545      /**
20546      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20547      *                        Roo.resizable.
20548      */
20549     resizable : false,
20550      /**
20551      * @cfg {Number} height (in pixels)
20552      */   
20553     height: 300,
20554    /**
20555      * @cfg {Number} width (in pixels)
20556      */   
20557     width: 500,
20558     
20559     /**
20560      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20561      * 
20562      */
20563     stylesheets: false,
20564     
20565     // id of frame..
20566     frameId: false,
20567     
20568     // private properties
20569     validationEvent : false,
20570     deferHeight: true,
20571     initialized : false,
20572     activated : false,
20573     sourceEditMode : false,
20574     onFocus : Roo.emptyFn,
20575     iframePad:3,
20576     hideMode:'offsets',
20577     
20578     clearUp: true,
20579     
20580     // blacklist + whitelisted elements..
20581     black: false,
20582     white: false,
20583      
20584     bodyCls : '',
20585
20586     /**
20587      * Protected method that will not generally be called directly. It
20588      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20589      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20590      */
20591     getDocMarkup : function(){
20592         // body styles..
20593         var st = '';
20594         
20595         // inherit styels from page...?? 
20596         if (this.stylesheets === false) {
20597             
20598             Roo.get(document.head).select('style').each(function(node) {
20599                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20600             });
20601             
20602             Roo.get(document.head).select('link').each(function(node) { 
20603                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20604             });
20605             
20606         } else if (!this.stylesheets.length) {
20607                 // simple..
20608                 st = '<style type="text/css">' +
20609                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20610                    '</style>';
20611         } else { 
20612             st = '<style type="text/css">' +
20613                     this.stylesheets +
20614                 '</style>';
20615         }
20616         
20617         st +=  '<style type="text/css">' +
20618             'IMG { cursor: pointer } ' +
20619         '</style>';
20620
20621         var cls = 'roo-htmleditor-body';
20622         
20623         if(this.bodyCls.length){
20624             cls += ' ' + this.bodyCls;
20625         }
20626         
20627         return '<html><head>' + st  +
20628             //<style type="text/css">' +
20629             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20630             //'</style>' +
20631             ' </head><body class="' +  cls + '"></body></html>';
20632     },
20633
20634     // private
20635     onRender : function(ct, position)
20636     {
20637         var _t = this;
20638         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20639         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20640         
20641         
20642         this.el.dom.style.border = '0 none';
20643         this.el.dom.setAttribute('tabIndex', -1);
20644         this.el.addClass('x-hidden hide');
20645         
20646         
20647         
20648         if(Roo.isIE){ // fix IE 1px bogus margin
20649             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20650         }
20651        
20652         
20653         this.frameId = Roo.id();
20654         
20655          
20656         
20657         var iframe = this.owner.wrap.createChild({
20658             tag: 'iframe',
20659             cls: 'form-control', // bootstrap..
20660             id: this.frameId,
20661             name: this.frameId,
20662             frameBorder : 'no',
20663             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20664         }, this.el
20665         );
20666         
20667         
20668         this.iframe = iframe.dom;
20669
20670          this.assignDocWin();
20671         
20672         this.doc.designMode = 'on';
20673        
20674         this.doc.open();
20675         this.doc.write(this.getDocMarkup());
20676         this.doc.close();
20677
20678         
20679         var task = { // must defer to wait for browser to be ready
20680             run : function(){
20681                 //console.log("run task?" + this.doc.readyState);
20682                 this.assignDocWin();
20683                 if(this.doc.body || this.doc.readyState == 'complete'){
20684                     try {
20685                         this.doc.designMode="on";
20686                     } catch (e) {
20687                         return;
20688                     }
20689                     Roo.TaskMgr.stop(task);
20690                     this.initEditor.defer(10, this);
20691                 }
20692             },
20693             interval : 10,
20694             duration: 10000,
20695             scope: this
20696         };
20697         Roo.TaskMgr.start(task);
20698
20699     },
20700
20701     // private
20702     onResize : function(w, h)
20703     {
20704          Roo.log('resize: ' +w + ',' + h );
20705         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20706         if(!this.iframe){
20707             return;
20708         }
20709         if(typeof w == 'number'){
20710             
20711             this.iframe.style.width = w + 'px';
20712         }
20713         if(typeof h == 'number'){
20714             
20715             this.iframe.style.height = h + 'px';
20716             if(this.doc){
20717                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20718             }
20719         }
20720         
20721     },
20722
20723     /**
20724      * Toggles the editor between standard and source edit mode.
20725      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20726      */
20727     toggleSourceEdit : function(sourceEditMode){
20728         
20729         this.sourceEditMode = sourceEditMode === true;
20730         
20731         if(this.sourceEditMode){
20732  
20733             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20734             
20735         }else{
20736             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20737             //this.iframe.className = '';
20738             this.deferFocus();
20739         }
20740         //this.setSize(this.owner.wrap.getSize());
20741         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20742     },
20743
20744     
20745   
20746
20747     /**
20748      * Protected method that will not generally be called directly. If you need/want
20749      * custom HTML cleanup, this is the method you should override.
20750      * @param {String} html The HTML to be cleaned
20751      * return {String} The cleaned HTML
20752      */
20753     cleanHtml : function(html){
20754         html = String(html);
20755         if(html.length > 5){
20756             if(Roo.isSafari){ // strip safari nonsense
20757                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20758             }
20759         }
20760         if(html == '&nbsp;'){
20761             html = '';
20762         }
20763         return html;
20764     },
20765
20766     /**
20767      * HTML Editor -> Textarea
20768      * Protected method that will not generally be called directly. Syncs the contents
20769      * of the editor iframe with the textarea.
20770      */
20771     syncValue : function(){
20772         if(this.initialized){
20773             var bd = (this.doc.body || this.doc.documentElement);
20774             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20775             var html = bd.innerHTML;
20776             if(Roo.isSafari){
20777                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20778                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20779                 if(m && m[1]){
20780                     html = '<div style="'+m[0]+'">' + html + '</div>';
20781                 }
20782             }
20783             html = this.cleanHtml(html);
20784             // fix up the special chars.. normaly like back quotes in word...
20785             // however we do not want to do this with chinese..
20786             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20787                 var cc = b.charCodeAt();
20788                 if (
20789                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20790                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20791                     (cc >= 0xf900 && cc < 0xfb00 )
20792                 ) {
20793                         return b;
20794                 }
20795                 return "&#"+cc+";" 
20796             });
20797             if(this.owner.fireEvent('beforesync', this, html) !== false){
20798                 this.el.dom.value = html;
20799                 this.owner.fireEvent('sync', this, html);
20800             }
20801         }
20802     },
20803
20804     /**
20805      * Protected method that will not generally be called directly. Pushes the value of the textarea
20806      * into the iframe editor.
20807      */
20808     pushValue : function(){
20809         if(this.initialized){
20810             var v = this.el.dom.value.trim();
20811             
20812 //            if(v.length < 1){
20813 //                v = '&#160;';
20814 //            }
20815             
20816             if(this.owner.fireEvent('beforepush', this, v) !== false){
20817                 var d = (this.doc.body || this.doc.documentElement);
20818                 d.innerHTML = v;
20819                 this.cleanUpPaste();
20820                 this.el.dom.value = d.innerHTML;
20821                 this.owner.fireEvent('push', this, v);
20822             }
20823         }
20824     },
20825
20826     // private
20827     deferFocus : function(){
20828         this.focus.defer(10, this);
20829     },
20830
20831     // doc'ed in Field
20832     focus : function(){
20833         if(this.win && !this.sourceEditMode){
20834             this.win.focus();
20835         }else{
20836             this.el.focus();
20837         }
20838     },
20839     
20840     assignDocWin: function()
20841     {
20842         var iframe = this.iframe;
20843         
20844          if(Roo.isIE){
20845             this.doc = iframe.contentWindow.document;
20846             this.win = iframe.contentWindow;
20847         } else {
20848 //            if (!Roo.get(this.frameId)) {
20849 //                return;
20850 //            }
20851 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20852 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20853             
20854             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20855                 return;
20856             }
20857             
20858             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20859             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20860         }
20861     },
20862     
20863     // private
20864     initEditor : function(){
20865         //console.log("INIT EDITOR");
20866         this.assignDocWin();
20867         
20868         
20869         
20870         this.doc.designMode="on";
20871         this.doc.open();
20872         this.doc.write(this.getDocMarkup());
20873         this.doc.close();
20874         
20875         var dbody = (this.doc.body || this.doc.documentElement);
20876         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20877         // this copies styles from the containing element into thsi one..
20878         // not sure why we need all of this..
20879         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20880         
20881         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20882         //ss['background-attachment'] = 'fixed'; // w3c
20883         dbody.bgProperties = 'fixed'; // ie
20884         //Roo.DomHelper.applyStyles(dbody, ss);
20885         Roo.EventManager.on(this.doc, {
20886             //'mousedown': this.onEditorEvent,
20887             'mouseup': this.onEditorEvent,
20888             'dblclick': this.onEditorEvent,
20889             'click': this.onEditorEvent,
20890             'keyup': this.onEditorEvent,
20891             buffer:100,
20892             scope: this
20893         });
20894         if(Roo.isGecko){
20895             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20896         }
20897         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20898             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20899         }
20900         this.initialized = true;
20901
20902         this.owner.fireEvent('initialize', this);
20903         this.pushValue();
20904     },
20905
20906     // private
20907     onDestroy : function(){
20908         
20909         
20910         
20911         if(this.rendered){
20912             
20913             //for (var i =0; i < this.toolbars.length;i++) {
20914             //    // fixme - ask toolbars for heights?
20915             //    this.toolbars[i].onDestroy();
20916            // }
20917             
20918             //this.wrap.dom.innerHTML = '';
20919             //this.wrap.remove();
20920         }
20921     },
20922
20923     // private
20924     onFirstFocus : function(){
20925         
20926         this.assignDocWin();
20927         
20928         
20929         this.activated = true;
20930          
20931     
20932         if(Roo.isGecko){ // prevent silly gecko errors
20933             this.win.focus();
20934             var s = this.win.getSelection();
20935             if(!s.focusNode || s.focusNode.nodeType != 3){
20936                 var r = s.getRangeAt(0);
20937                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20938                 r.collapse(true);
20939                 this.deferFocus();
20940             }
20941             try{
20942                 this.execCmd('useCSS', true);
20943                 this.execCmd('styleWithCSS', false);
20944             }catch(e){}
20945         }
20946         this.owner.fireEvent('activate', this);
20947     },
20948
20949     // private
20950     adjustFont: function(btn){
20951         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20952         //if(Roo.isSafari){ // safari
20953         //    adjust *= 2;
20954        // }
20955         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20956         if(Roo.isSafari){ // safari
20957             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20958             v =  (v < 10) ? 10 : v;
20959             v =  (v > 48) ? 48 : v;
20960             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20961             
20962         }
20963         
20964         
20965         v = Math.max(1, v+adjust);
20966         
20967         this.execCmd('FontSize', v  );
20968     },
20969
20970     onEditorEvent : function(e)
20971     {
20972         this.owner.fireEvent('editorevent', this, e);
20973       //  this.updateToolbar();
20974         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20975     },
20976
20977     insertTag : function(tg)
20978     {
20979         // could be a bit smarter... -> wrap the current selected tRoo..
20980         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20981             
20982             range = this.createRange(this.getSelection());
20983             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20984             wrappingNode.appendChild(range.extractContents());
20985             range.insertNode(wrappingNode);
20986
20987             return;
20988             
20989             
20990             
20991         }
20992         this.execCmd("formatblock",   tg);
20993         
20994     },
20995     
20996     insertText : function(txt)
20997     {
20998         
20999         
21000         var range = this.createRange();
21001         range.deleteContents();
21002                //alert(Sender.getAttribute('label'));
21003                
21004         range.insertNode(this.doc.createTextNode(txt));
21005     } ,
21006     
21007      
21008
21009     /**
21010      * Executes a Midas editor command on the editor document and performs necessary focus and
21011      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21012      * @param {String} cmd The Midas command
21013      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21014      */
21015     relayCmd : function(cmd, value){
21016         this.win.focus();
21017         this.execCmd(cmd, value);
21018         this.owner.fireEvent('editorevent', this);
21019         //this.updateToolbar();
21020         this.owner.deferFocus();
21021     },
21022
21023     /**
21024      * Executes a Midas editor command directly on the editor document.
21025      * For visual commands, you should use {@link #relayCmd} instead.
21026      * <b>This should only be called after the editor is initialized.</b>
21027      * @param {String} cmd The Midas command
21028      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21029      */
21030     execCmd : function(cmd, value){
21031         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21032         this.syncValue();
21033     },
21034  
21035  
21036    
21037     /**
21038      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21039      * to insert tRoo.
21040      * @param {String} text | dom node.. 
21041      */
21042     insertAtCursor : function(text)
21043     {
21044         
21045         if(!this.activated){
21046             return;
21047         }
21048         /*
21049         if(Roo.isIE){
21050             this.win.focus();
21051             var r = this.doc.selection.createRange();
21052             if(r){
21053                 r.collapse(true);
21054                 r.pasteHTML(text);
21055                 this.syncValue();
21056                 this.deferFocus();
21057             
21058             }
21059             return;
21060         }
21061         */
21062         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21063             this.win.focus();
21064             
21065             
21066             // from jquery ui (MIT licenced)
21067             var range, node;
21068             var win = this.win;
21069             
21070             if (win.getSelection && win.getSelection().getRangeAt) {
21071                 range = win.getSelection().getRangeAt(0);
21072                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21073                 range.insertNode(node);
21074             } else if (win.document.selection && win.document.selection.createRange) {
21075                 // no firefox support
21076                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21077                 win.document.selection.createRange().pasteHTML(txt);
21078             } else {
21079                 // no firefox support
21080                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21081                 this.execCmd('InsertHTML', txt);
21082             } 
21083             
21084             this.syncValue();
21085             
21086             this.deferFocus();
21087         }
21088     },
21089  // private
21090     mozKeyPress : function(e){
21091         if(e.ctrlKey){
21092             var c = e.getCharCode(), cmd;
21093           
21094             if(c > 0){
21095                 c = String.fromCharCode(c).toLowerCase();
21096                 switch(c){
21097                     case 'b':
21098                         cmd = 'bold';
21099                         break;
21100                     case 'i':
21101                         cmd = 'italic';
21102                         break;
21103                     
21104                     case 'u':
21105                         cmd = 'underline';
21106                         break;
21107                     
21108                     case 'v':
21109                         this.cleanUpPaste.defer(100, this);
21110                         return;
21111                         
21112                 }
21113                 if(cmd){
21114                     this.win.focus();
21115                     this.execCmd(cmd);
21116                     this.deferFocus();
21117                     e.preventDefault();
21118                 }
21119                 
21120             }
21121         }
21122     },
21123
21124     // private
21125     fixKeys : function(){ // load time branching for fastest keydown performance
21126         if(Roo.isIE){
21127             return function(e){
21128                 var k = e.getKey(), r;
21129                 if(k == e.TAB){
21130                     e.stopEvent();
21131                     r = this.doc.selection.createRange();
21132                     if(r){
21133                         r.collapse(true);
21134                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21135                         this.deferFocus();
21136                     }
21137                     return;
21138                 }
21139                 
21140                 if(k == e.ENTER){
21141                     r = this.doc.selection.createRange();
21142                     if(r){
21143                         var target = r.parentElement();
21144                         if(!target || target.tagName.toLowerCase() != 'li'){
21145                             e.stopEvent();
21146                             r.pasteHTML('<br />');
21147                             r.collapse(false);
21148                             r.select();
21149                         }
21150                     }
21151                 }
21152                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21153                     this.cleanUpPaste.defer(100, this);
21154                     return;
21155                 }
21156                 
21157                 
21158             };
21159         }else if(Roo.isOpera){
21160             return function(e){
21161                 var k = e.getKey();
21162                 if(k == e.TAB){
21163                     e.stopEvent();
21164                     this.win.focus();
21165                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21166                     this.deferFocus();
21167                 }
21168                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21169                     this.cleanUpPaste.defer(100, this);
21170                     return;
21171                 }
21172                 
21173             };
21174         }else if(Roo.isSafari){
21175             return function(e){
21176                 var k = e.getKey();
21177                 
21178                 if(k == e.TAB){
21179                     e.stopEvent();
21180                     this.execCmd('InsertText','\t');
21181                     this.deferFocus();
21182                     return;
21183                 }
21184                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21185                     this.cleanUpPaste.defer(100, this);
21186                     return;
21187                 }
21188                 
21189              };
21190         }
21191     }(),
21192     
21193     getAllAncestors: function()
21194     {
21195         var p = this.getSelectedNode();
21196         var a = [];
21197         if (!p) {
21198             a.push(p); // push blank onto stack..
21199             p = this.getParentElement();
21200         }
21201         
21202         
21203         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21204             a.push(p);
21205             p = p.parentNode;
21206         }
21207         a.push(this.doc.body);
21208         return a;
21209     },
21210     lastSel : false,
21211     lastSelNode : false,
21212     
21213     
21214     getSelection : function() 
21215     {
21216         this.assignDocWin();
21217         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21218     },
21219     
21220     getSelectedNode: function() 
21221     {
21222         // this may only work on Gecko!!!
21223         
21224         // should we cache this!!!!
21225         
21226         
21227         
21228          
21229         var range = this.createRange(this.getSelection()).cloneRange();
21230         
21231         if (Roo.isIE) {
21232             var parent = range.parentElement();
21233             while (true) {
21234                 var testRange = range.duplicate();
21235                 testRange.moveToElementText(parent);
21236                 if (testRange.inRange(range)) {
21237                     break;
21238                 }
21239                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21240                     break;
21241                 }
21242                 parent = parent.parentElement;
21243             }
21244             return parent;
21245         }
21246         
21247         // is ancestor a text element.
21248         var ac =  range.commonAncestorContainer;
21249         if (ac.nodeType == 3) {
21250             ac = ac.parentNode;
21251         }
21252         
21253         var ar = ac.childNodes;
21254          
21255         var nodes = [];
21256         var other_nodes = [];
21257         var has_other_nodes = false;
21258         for (var i=0;i<ar.length;i++) {
21259             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21260                 continue;
21261             }
21262             // fullly contained node.
21263             
21264             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21265                 nodes.push(ar[i]);
21266                 continue;
21267             }
21268             
21269             // probably selected..
21270             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21271                 other_nodes.push(ar[i]);
21272                 continue;
21273             }
21274             // outer..
21275             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21276                 continue;
21277             }
21278             
21279             
21280             has_other_nodes = true;
21281         }
21282         if (!nodes.length && other_nodes.length) {
21283             nodes= other_nodes;
21284         }
21285         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21286             return false;
21287         }
21288         
21289         return nodes[0];
21290     },
21291     createRange: function(sel)
21292     {
21293         // this has strange effects when using with 
21294         // top toolbar - not sure if it's a great idea.
21295         //this.editor.contentWindow.focus();
21296         if (typeof sel != "undefined") {
21297             try {
21298                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21299             } catch(e) {
21300                 return this.doc.createRange();
21301             }
21302         } else {
21303             return this.doc.createRange();
21304         }
21305     },
21306     getParentElement: function()
21307     {
21308         
21309         this.assignDocWin();
21310         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21311         
21312         var range = this.createRange(sel);
21313          
21314         try {
21315             var p = range.commonAncestorContainer;
21316             while (p.nodeType == 3) { // text node
21317                 p = p.parentNode;
21318             }
21319             return p;
21320         } catch (e) {
21321             return null;
21322         }
21323     
21324     },
21325     /***
21326      *
21327      * Range intersection.. the hard stuff...
21328      *  '-1' = before
21329      *  '0' = hits..
21330      *  '1' = after.
21331      *         [ -- selected range --- ]
21332      *   [fail]                        [fail]
21333      *
21334      *    basically..
21335      *      if end is before start or  hits it. fail.
21336      *      if start is after end or hits it fail.
21337      *
21338      *   if either hits (but other is outside. - then it's not 
21339      *   
21340      *    
21341      **/
21342     
21343     
21344     // @see http://www.thismuchiknow.co.uk/?p=64.
21345     rangeIntersectsNode : function(range, node)
21346     {
21347         var nodeRange = node.ownerDocument.createRange();
21348         try {
21349             nodeRange.selectNode(node);
21350         } catch (e) {
21351             nodeRange.selectNodeContents(node);
21352         }
21353     
21354         var rangeStartRange = range.cloneRange();
21355         rangeStartRange.collapse(true);
21356     
21357         var rangeEndRange = range.cloneRange();
21358         rangeEndRange.collapse(false);
21359     
21360         var nodeStartRange = nodeRange.cloneRange();
21361         nodeStartRange.collapse(true);
21362     
21363         var nodeEndRange = nodeRange.cloneRange();
21364         nodeEndRange.collapse(false);
21365     
21366         return rangeStartRange.compareBoundaryPoints(
21367                  Range.START_TO_START, nodeEndRange) == -1 &&
21368                rangeEndRange.compareBoundaryPoints(
21369                  Range.START_TO_START, nodeStartRange) == 1;
21370         
21371          
21372     },
21373     rangeCompareNode : function(range, node)
21374     {
21375         var nodeRange = node.ownerDocument.createRange();
21376         try {
21377             nodeRange.selectNode(node);
21378         } catch (e) {
21379             nodeRange.selectNodeContents(node);
21380         }
21381         
21382         
21383         range.collapse(true);
21384     
21385         nodeRange.collapse(true);
21386      
21387         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21388         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21389          
21390         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21391         
21392         var nodeIsBefore   =  ss == 1;
21393         var nodeIsAfter    = ee == -1;
21394         
21395         if (nodeIsBefore && nodeIsAfter) {
21396             return 0; // outer
21397         }
21398         if (!nodeIsBefore && nodeIsAfter) {
21399             return 1; //right trailed.
21400         }
21401         
21402         if (nodeIsBefore && !nodeIsAfter) {
21403             return 2;  // left trailed.
21404         }
21405         // fully contined.
21406         return 3;
21407     },
21408
21409     // private? - in a new class?
21410     cleanUpPaste :  function()
21411     {
21412         // cleans up the whole document..
21413         Roo.log('cleanuppaste');
21414         
21415         this.cleanUpChildren(this.doc.body);
21416         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21417         if (clean != this.doc.body.innerHTML) {
21418             this.doc.body.innerHTML = clean;
21419         }
21420         
21421     },
21422     
21423     cleanWordChars : function(input) {// change the chars to hex code
21424         var he = Roo.HtmlEditorCore;
21425         
21426         var output = input;
21427         Roo.each(he.swapCodes, function(sw) { 
21428             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21429             
21430             output = output.replace(swapper, sw[1]);
21431         });
21432         
21433         return output;
21434     },
21435     
21436     
21437     cleanUpChildren : function (n)
21438     {
21439         if (!n.childNodes.length) {
21440             return;
21441         }
21442         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21443            this.cleanUpChild(n.childNodes[i]);
21444         }
21445     },
21446     
21447     
21448         
21449     
21450     cleanUpChild : function (node)
21451     {
21452         var ed = this;
21453         //console.log(node);
21454         if (node.nodeName == "#text") {
21455             // clean up silly Windows -- stuff?
21456             return; 
21457         }
21458         if (node.nodeName == "#comment") {
21459             node.parentNode.removeChild(node);
21460             // clean up silly Windows -- stuff?
21461             return; 
21462         }
21463         var lcname = node.tagName.toLowerCase();
21464         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21465         // whitelist of tags..
21466         
21467         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21468             // remove node.
21469             node.parentNode.removeChild(node);
21470             return;
21471             
21472         }
21473         
21474         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21475         
21476         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21477         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21478         
21479         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21480         //    remove_keep_children = true;
21481         //}
21482         
21483         if (remove_keep_children) {
21484             this.cleanUpChildren(node);
21485             // inserts everything just before this node...
21486             while (node.childNodes.length) {
21487                 var cn = node.childNodes[0];
21488                 node.removeChild(cn);
21489                 node.parentNode.insertBefore(cn, node);
21490             }
21491             node.parentNode.removeChild(node);
21492             return;
21493         }
21494         
21495         if (!node.attributes || !node.attributes.length) {
21496             this.cleanUpChildren(node);
21497             return;
21498         }
21499         
21500         function cleanAttr(n,v)
21501         {
21502             
21503             if (v.match(/^\./) || v.match(/^\//)) {
21504                 return;
21505             }
21506             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21507                 return;
21508             }
21509             if (v.match(/^#/)) {
21510                 return;
21511             }
21512 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21513             node.removeAttribute(n);
21514             
21515         }
21516         
21517         var cwhite = this.cwhite;
21518         var cblack = this.cblack;
21519             
21520         function cleanStyle(n,v)
21521         {
21522             if (v.match(/expression/)) { //XSS?? should we even bother..
21523                 node.removeAttribute(n);
21524                 return;
21525             }
21526             
21527             var parts = v.split(/;/);
21528             var clean = [];
21529             
21530             Roo.each(parts, function(p) {
21531                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21532                 if (!p.length) {
21533                     return true;
21534                 }
21535                 var l = p.split(':').shift().replace(/\s+/g,'');
21536                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21537                 
21538                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21539 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21540                     //node.removeAttribute(n);
21541                     return true;
21542                 }
21543                 //Roo.log()
21544                 // only allow 'c whitelisted system attributes'
21545                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21546 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21547                     //node.removeAttribute(n);
21548                     return true;
21549                 }
21550                 
21551                 
21552                  
21553                 
21554                 clean.push(p);
21555                 return true;
21556             });
21557             if (clean.length) { 
21558                 node.setAttribute(n, clean.join(';'));
21559             } else {
21560                 node.removeAttribute(n);
21561             }
21562             
21563         }
21564         
21565         
21566         for (var i = node.attributes.length-1; i > -1 ; i--) {
21567             var a = node.attributes[i];
21568             //console.log(a);
21569             
21570             if (a.name.toLowerCase().substr(0,2)=='on')  {
21571                 node.removeAttribute(a.name);
21572                 continue;
21573             }
21574             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21575                 node.removeAttribute(a.name);
21576                 continue;
21577             }
21578             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21579                 cleanAttr(a.name,a.value); // fixme..
21580                 continue;
21581             }
21582             if (a.name == 'style') {
21583                 cleanStyle(a.name,a.value);
21584                 continue;
21585             }
21586             /// clean up MS crap..
21587             // tecnically this should be a list of valid class'es..
21588             
21589             
21590             if (a.name == 'class') {
21591                 if (a.value.match(/^Mso/)) {
21592                     node.className = '';
21593                 }
21594                 
21595                 if (a.value.match(/^body$/)) {
21596                     node.className = '';
21597                 }
21598                 continue;
21599             }
21600             
21601             // style cleanup!?
21602             // class cleanup?
21603             
21604         }
21605         
21606         
21607         this.cleanUpChildren(node);
21608         
21609         
21610     },
21611     
21612     /**
21613      * Clean up MS wordisms...
21614      */
21615     cleanWord : function(node)
21616     {
21617         if (!node) {
21618             this.cleanWord(this.doc.body);
21619             return;
21620         }
21621         
21622         if(
21623                 node.nodeName == 'SPAN' &&
21624                 !node.hasAttributes() &&
21625                 node.childNodes.length == 1 &&
21626                 node.firstChild.nodeName == "#text"
21627         ) {
21628             var text = ' ' + node.innerHTML + ' ';
21629             var textNode = document.createTextNode(text);
21630             node.parentNode.insertBefore(textNode, node);
21631             node.parentNode.removeChild(node);
21632         }
21633         
21634         if (node.nodeName == "#text") {
21635             // clean up silly Windows -- stuff?
21636             return; 
21637         }
21638         if (node.nodeName == "#comment") {
21639             node.parentNode.removeChild(node);
21640             // clean up silly Windows -- stuff?
21641             return; 
21642         }
21643         
21644         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21645             node.parentNode.removeChild(node);
21646             return;
21647         }
21648         
21649         // remove - but keep children..
21650         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21651             while (node.childNodes.length) {
21652                 var cn = node.childNodes[0];
21653                 node.removeChild(cn);
21654                 node.parentNode.insertBefore(cn, node);
21655             }
21656             node.parentNode.removeChild(node);
21657             this.iterateChildren(node, this.cleanWord);
21658             return;
21659         }
21660         // clean styles
21661         if (node.className.length) {
21662             
21663             var cn = node.className.split(/\W+/);
21664             var cna = [];
21665             Roo.each(cn, function(cls) {
21666                 if (cls.match(/Mso[a-zA-Z]+/)) {
21667                     return;
21668                 }
21669                 cna.push(cls);
21670             });
21671             node.className = cna.length ? cna.join(' ') : '';
21672             if (!cna.length) {
21673                 node.removeAttribute("class");
21674             }
21675         }
21676         
21677         if (node.hasAttribute("lang")) {
21678             node.removeAttribute("lang");
21679         }
21680         
21681         if (node.hasAttribute("style")) {
21682             
21683             var styles = node.getAttribute("style").split(";");
21684             var nstyle = [];
21685             Roo.each(styles, function(s) {
21686                 if (!s.match(/:/)) {
21687                     return;
21688                 }
21689                 var kv = s.split(":");
21690                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21691                     return;
21692                 }
21693                 // what ever is left... we allow.
21694                 nstyle.push(s);
21695             });
21696             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21697             if (!nstyle.length) {
21698                 node.removeAttribute('style');
21699             }
21700         }
21701         this.iterateChildren(node, this.cleanWord);
21702         
21703         
21704         
21705     },
21706     /**
21707      * iterateChildren of a Node, calling fn each time, using this as the scole..
21708      * @param {DomNode} node node to iterate children of.
21709      * @param {Function} fn method of this class to call on each item.
21710      */
21711     iterateChildren : function(node, fn)
21712     {
21713         if (!node.childNodes.length) {
21714                 return;
21715         }
21716         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21717            fn.call(this, node.childNodes[i])
21718         }
21719     },
21720     
21721     
21722     /**
21723      * cleanTableWidths.
21724      *
21725      * Quite often pasting from word etc.. results in tables with column and widths.
21726      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21727      *
21728      */
21729     cleanTableWidths : function(node)
21730     {
21731          
21732          
21733         if (!node) {
21734             this.cleanTableWidths(this.doc.body);
21735             return;
21736         }
21737         
21738         // ignore list...
21739         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21740             return; 
21741         }
21742         Roo.log(node.tagName);
21743         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21744             this.iterateChildren(node, this.cleanTableWidths);
21745             return;
21746         }
21747         if (node.hasAttribute('width')) {
21748             node.removeAttribute('width');
21749         }
21750         
21751          
21752         if (node.hasAttribute("style")) {
21753             // pretty basic...
21754             
21755             var styles = node.getAttribute("style").split(";");
21756             var nstyle = [];
21757             Roo.each(styles, function(s) {
21758                 if (!s.match(/:/)) {
21759                     return;
21760                 }
21761                 var kv = s.split(":");
21762                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21763                     return;
21764                 }
21765                 // what ever is left... we allow.
21766                 nstyle.push(s);
21767             });
21768             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21769             if (!nstyle.length) {
21770                 node.removeAttribute('style');
21771             }
21772         }
21773         
21774         this.iterateChildren(node, this.cleanTableWidths);
21775         
21776         
21777     },
21778     
21779     
21780     
21781     
21782     domToHTML : function(currentElement, depth, nopadtext) {
21783         
21784         depth = depth || 0;
21785         nopadtext = nopadtext || false;
21786     
21787         if (!currentElement) {
21788             return this.domToHTML(this.doc.body);
21789         }
21790         
21791         //Roo.log(currentElement);
21792         var j;
21793         var allText = false;
21794         var nodeName = currentElement.nodeName;
21795         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21796         
21797         if  (nodeName == '#text') {
21798             
21799             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21800         }
21801         
21802         
21803         var ret = '';
21804         if (nodeName != 'BODY') {
21805              
21806             var i = 0;
21807             // Prints the node tagName, such as <A>, <IMG>, etc
21808             if (tagName) {
21809                 var attr = [];
21810                 for(i = 0; i < currentElement.attributes.length;i++) {
21811                     // quoting?
21812                     var aname = currentElement.attributes.item(i).name;
21813                     if (!currentElement.attributes.item(i).value.length) {
21814                         continue;
21815                     }
21816                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21817                 }
21818                 
21819                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21820             } 
21821             else {
21822                 
21823                 // eack
21824             }
21825         } else {
21826             tagName = false;
21827         }
21828         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21829             return ret;
21830         }
21831         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21832             nopadtext = true;
21833         }
21834         
21835         
21836         // Traverse the tree
21837         i = 0;
21838         var currentElementChild = currentElement.childNodes.item(i);
21839         var allText = true;
21840         var innerHTML  = '';
21841         lastnode = '';
21842         while (currentElementChild) {
21843             // Formatting code (indent the tree so it looks nice on the screen)
21844             var nopad = nopadtext;
21845             if (lastnode == 'SPAN') {
21846                 nopad  = true;
21847             }
21848             // text
21849             if  (currentElementChild.nodeName == '#text') {
21850                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21851                 toadd = nopadtext ? toadd : toadd.trim();
21852                 if (!nopad && toadd.length > 80) {
21853                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21854                 }
21855                 innerHTML  += toadd;
21856                 
21857                 i++;
21858                 currentElementChild = currentElement.childNodes.item(i);
21859                 lastNode = '';
21860                 continue;
21861             }
21862             allText = false;
21863             
21864             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21865                 
21866             // Recursively traverse the tree structure of the child node
21867             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21868             lastnode = currentElementChild.nodeName;
21869             i++;
21870             currentElementChild=currentElement.childNodes.item(i);
21871         }
21872         
21873         ret += innerHTML;
21874         
21875         if (!allText) {
21876                 // The remaining code is mostly for formatting the tree
21877             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21878         }
21879         
21880         
21881         if (tagName) {
21882             ret+= "</"+tagName+">";
21883         }
21884         return ret;
21885         
21886     },
21887         
21888     applyBlacklists : function()
21889     {
21890         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21891         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21892         
21893         this.white = [];
21894         this.black = [];
21895         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21896             if (b.indexOf(tag) > -1) {
21897                 return;
21898             }
21899             this.white.push(tag);
21900             
21901         }, this);
21902         
21903         Roo.each(w, function(tag) {
21904             if (b.indexOf(tag) > -1) {
21905                 return;
21906             }
21907             if (this.white.indexOf(tag) > -1) {
21908                 return;
21909             }
21910             this.white.push(tag);
21911             
21912         }, this);
21913         
21914         
21915         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21916             if (w.indexOf(tag) > -1) {
21917                 return;
21918             }
21919             this.black.push(tag);
21920             
21921         }, this);
21922         
21923         Roo.each(b, function(tag) {
21924             if (w.indexOf(tag) > -1) {
21925                 return;
21926             }
21927             if (this.black.indexOf(tag) > -1) {
21928                 return;
21929             }
21930             this.black.push(tag);
21931             
21932         }, this);
21933         
21934         
21935         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21936         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21937         
21938         this.cwhite = [];
21939         this.cblack = [];
21940         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21941             if (b.indexOf(tag) > -1) {
21942                 return;
21943             }
21944             this.cwhite.push(tag);
21945             
21946         }, this);
21947         
21948         Roo.each(w, function(tag) {
21949             if (b.indexOf(tag) > -1) {
21950                 return;
21951             }
21952             if (this.cwhite.indexOf(tag) > -1) {
21953                 return;
21954             }
21955             this.cwhite.push(tag);
21956             
21957         }, this);
21958         
21959         
21960         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21961             if (w.indexOf(tag) > -1) {
21962                 return;
21963             }
21964             this.cblack.push(tag);
21965             
21966         }, this);
21967         
21968         Roo.each(b, function(tag) {
21969             if (w.indexOf(tag) > -1) {
21970                 return;
21971             }
21972             if (this.cblack.indexOf(tag) > -1) {
21973                 return;
21974             }
21975             this.cblack.push(tag);
21976             
21977         }, this);
21978     },
21979     
21980     setStylesheets : function(stylesheets)
21981     {
21982         if(typeof(stylesheets) == 'string'){
21983             Roo.get(this.iframe.contentDocument.head).createChild({
21984                 tag : 'link',
21985                 rel : 'stylesheet',
21986                 type : 'text/css',
21987                 href : stylesheets
21988             });
21989             
21990             return;
21991         }
21992         var _this = this;
21993      
21994         Roo.each(stylesheets, function(s) {
21995             if(!s.length){
21996                 return;
21997             }
21998             
21999             Roo.get(_this.iframe.contentDocument.head).createChild({
22000                 tag : 'link',
22001                 rel : 'stylesheet',
22002                 type : 'text/css',
22003                 href : s
22004             });
22005         });
22006
22007         
22008     },
22009     
22010     removeStylesheets : function()
22011     {
22012         var _this = this;
22013         
22014         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22015             s.remove();
22016         });
22017     },
22018     
22019     setStyle : function(style)
22020     {
22021         Roo.get(this.iframe.contentDocument.head).createChild({
22022             tag : 'style',
22023             type : 'text/css',
22024             html : style
22025         });
22026
22027         return;
22028     }
22029     
22030     // hide stuff that is not compatible
22031     /**
22032      * @event blur
22033      * @hide
22034      */
22035     /**
22036      * @event change
22037      * @hide
22038      */
22039     /**
22040      * @event focus
22041      * @hide
22042      */
22043     /**
22044      * @event specialkey
22045      * @hide
22046      */
22047     /**
22048      * @cfg {String} fieldClass @hide
22049      */
22050     /**
22051      * @cfg {String} focusClass @hide
22052      */
22053     /**
22054      * @cfg {String} autoCreate @hide
22055      */
22056     /**
22057      * @cfg {String} inputType @hide
22058      */
22059     /**
22060      * @cfg {String} invalidClass @hide
22061      */
22062     /**
22063      * @cfg {String} invalidText @hide
22064      */
22065     /**
22066      * @cfg {String} msgFx @hide
22067      */
22068     /**
22069      * @cfg {String} validateOnBlur @hide
22070      */
22071 });
22072
22073 Roo.HtmlEditorCore.white = [
22074         'area', 'br', 'img', 'input', 'hr', 'wbr',
22075         
22076        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22077        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22078        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22079        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22080        'table',   'ul',         'xmp', 
22081        
22082        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22083       'thead',   'tr', 
22084      
22085       'dir', 'menu', 'ol', 'ul', 'dl',
22086        
22087       'embed',  'object'
22088 ];
22089
22090
22091 Roo.HtmlEditorCore.black = [
22092     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22093         'applet', // 
22094         'base',   'basefont', 'bgsound', 'blink',  'body', 
22095         'frame',  'frameset', 'head',    'html',   'ilayer', 
22096         'iframe', 'layer',  'link',     'meta',    'object',   
22097         'script', 'style' ,'title',  'xml' // clean later..
22098 ];
22099 Roo.HtmlEditorCore.clean = [
22100     'script', 'style', 'title', 'xml'
22101 ];
22102 Roo.HtmlEditorCore.remove = [
22103     'font'
22104 ];
22105 // attributes..
22106
22107 Roo.HtmlEditorCore.ablack = [
22108     'on'
22109 ];
22110     
22111 Roo.HtmlEditorCore.aclean = [ 
22112     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22113 ];
22114
22115 // protocols..
22116 Roo.HtmlEditorCore.pwhite= [
22117         'http',  'https',  'mailto'
22118 ];
22119
22120 // white listed style attributes.
22121 Roo.HtmlEditorCore.cwhite= [
22122       //  'text-align', /// default is to allow most things..
22123       
22124          
22125 //        'font-size'//??
22126 ];
22127
22128 // black listed style attributes.
22129 Roo.HtmlEditorCore.cblack= [
22130       //  'font-size' -- this can be set by the project 
22131 ];
22132
22133
22134 Roo.HtmlEditorCore.swapCodes   =[ 
22135     [    8211, "--" ], 
22136     [    8212, "--" ], 
22137     [    8216,  "'" ],  
22138     [    8217, "'" ],  
22139     [    8220, '"' ],  
22140     [    8221, '"' ],  
22141     [    8226, "*" ],  
22142     [    8230, "..." ]
22143 ]; 
22144
22145     //<script type="text/javascript">
22146
22147 /*
22148  * Ext JS Library 1.1.1
22149  * Copyright(c) 2006-2007, Ext JS, LLC.
22150  * Licence LGPL
22151  * 
22152  */
22153  
22154  
22155 Roo.form.HtmlEditor = function(config){
22156     
22157     
22158     
22159     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22160     
22161     if (!this.toolbars) {
22162         this.toolbars = [];
22163     }
22164     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22165     
22166     
22167 };
22168
22169 /**
22170  * @class Roo.form.HtmlEditor
22171  * @extends Roo.form.Field
22172  * Provides a lightweight HTML Editor component.
22173  *
22174  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22175  * 
22176  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22177  * supported by this editor.</b><br/><br/>
22178  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22179  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22180  */
22181 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22182     /**
22183      * @cfg {Boolean} clearUp
22184      */
22185     clearUp : true,
22186       /**
22187      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22188      */
22189     toolbars : false,
22190    
22191      /**
22192      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22193      *                        Roo.resizable.
22194      */
22195     resizable : false,
22196      /**
22197      * @cfg {Number} height (in pixels)
22198      */   
22199     height: 300,
22200    /**
22201      * @cfg {Number} width (in pixels)
22202      */   
22203     width: 500,
22204     
22205     /**
22206      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22207      * 
22208      */
22209     stylesheets: false,
22210     
22211     
22212      /**
22213      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22214      * 
22215      */
22216     cblack: false,
22217     /**
22218      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22219      * 
22220      */
22221     cwhite: false,
22222     
22223      /**
22224      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22225      * 
22226      */
22227     black: false,
22228     /**
22229      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22230      * 
22231      */
22232     white: false,
22233     
22234     // id of frame..
22235     frameId: false,
22236     
22237     // private properties
22238     validationEvent : false,
22239     deferHeight: true,
22240     initialized : false,
22241     activated : false,
22242     
22243     onFocus : Roo.emptyFn,
22244     iframePad:3,
22245     hideMode:'offsets',
22246     
22247     actionMode : 'container', // defaults to hiding it...
22248     
22249     defaultAutoCreate : { // modified by initCompnoent..
22250         tag: "textarea",
22251         style:"width:500px;height:300px;",
22252         autocomplete: "new-password"
22253     },
22254
22255     // private
22256     initComponent : function(){
22257         this.addEvents({
22258             /**
22259              * @event initialize
22260              * Fires when the editor is fully initialized (including the iframe)
22261              * @param {HtmlEditor} this
22262              */
22263             initialize: true,
22264             /**
22265              * @event activate
22266              * Fires when the editor is first receives the focus. Any insertion must wait
22267              * until after this event.
22268              * @param {HtmlEditor} this
22269              */
22270             activate: true,
22271              /**
22272              * @event beforesync
22273              * Fires before the textarea is updated with content from the editor iframe. Return false
22274              * to cancel the sync.
22275              * @param {HtmlEditor} this
22276              * @param {String} html
22277              */
22278             beforesync: true,
22279              /**
22280              * @event beforepush
22281              * Fires before the iframe editor is updated with content from the textarea. Return false
22282              * to cancel the push.
22283              * @param {HtmlEditor} this
22284              * @param {String} html
22285              */
22286             beforepush: true,
22287              /**
22288              * @event sync
22289              * Fires when the textarea is updated with content from the editor iframe.
22290              * @param {HtmlEditor} this
22291              * @param {String} html
22292              */
22293             sync: true,
22294              /**
22295              * @event push
22296              * Fires when the iframe editor is updated with content from the textarea.
22297              * @param {HtmlEditor} this
22298              * @param {String} html
22299              */
22300             push: true,
22301              /**
22302              * @event editmodechange
22303              * Fires when the editor switches edit modes
22304              * @param {HtmlEditor} this
22305              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22306              */
22307             editmodechange: true,
22308             /**
22309              * @event editorevent
22310              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22311              * @param {HtmlEditor} this
22312              */
22313             editorevent: true,
22314             /**
22315              * @event firstfocus
22316              * Fires when on first focus - needed by toolbars..
22317              * @param {HtmlEditor} this
22318              */
22319             firstfocus: true,
22320             /**
22321              * @event autosave
22322              * Auto save the htmlEditor value as a file into Events
22323              * @param {HtmlEditor} this
22324              */
22325             autosave: true,
22326             /**
22327              * @event savedpreview
22328              * preview the saved version of htmlEditor
22329              * @param {HtmlEditor} this
22330              */
22331             savedpreview: true,
22332             
22333             /**
22334             * @event stylesheetsclick
22335             * Fires when press the Sytlesheets button
22336             * @param {Roo.HtmlEditorCore} this
22337             */
22338             stylesheetsclick: true
22339         });
22340         this.defaultAutoCreate =  {
22341             tag: "textarea",
22342             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22343             autocomplete: "new-password"
22344         };
22345     },
22346
22347     /**
22348      * Protected method that will not generally be called directly. It
22349      * is called when the editor creates its toolbar. Override this method if you need to
22350      * add custom toolbar buttons.
22351      * @param {HtmlEditor} editor
22352      */
22353     createToolbar : function(editor){
22354         Roo.log("create toolbars");
22355         if (!editor.toolbars || !editor.toolbars.length) {
22356             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22357         }
22358         
22359         for (var i =0 ; i < editor.toolbars.length;i++) {
22360             editor.toolbars[i] = Roo.factory(
22361                     typeof(editor.toolbars[i]) == 'string' ?
22362                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22363                 Roo.form.HtmlEditor);
22364             editor.toolbars[i].init(editor);
22365         }
22366          
22367         
22368     },
22369
22370      
22371     // private
22372     onRender : function(ct, position)
22373     {
22374         var _t = this;
22375         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22376         
22377         this.wrap = this.el.wrap({
22378             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22379         });
22380         
22381         this.editorcore.onRender(ct, position);
22382          
22383         if (this.resizable) {
22384             this.resizeEl = new Roo.Resizable(this.wrap, {
22385                 pinned : true,
22386                 wrap: true,
22387                 dynamic : true,
22388                 minHeight : this.height,
22389                 height: this.height,
22390                 handles : this.resizable,
22391                 width: this.width,
22392                 listeners : {
22393                     resize : function(r, w, h) {
22394                         _t.onResize(w,h); // -something
22395                     }
22396                 }
22397             });
22398             
22399         }
22400         this.createToolbar(this);
22401        
22402         
22403         if(!this.width){
22404             this.setSize(this.wrap.getSize());
22405         }
22406         if (this.resizeEl) {
22407             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22408             // should trigger onReize..
22409         }
22410         
22411         this.keyNav = new Roo.KeyNav(this.el, {
22412             
22413             "tab" : function(e){
22414                 e.preventDefault();
22415                 
22416                 var value = this.getValue();
22417                 
22418                 var start = this.el.dom.selectionStart;
22419                 var end = this.el.dom.selectionEnd;
22420                 
22421                 if(!e.shiftKey){
22422                     
22423                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22424                     this.el.dom.setSelectionRange(end + 1, end + 1);
22425                     return;
22426                 }
22427                 
22428                 var f = value.substring(0, start).split("\t");
22429                 
22430                 if(f.pop().length != 0){
22431                     return;
22432                 }
22433                 
22434                 this.setValue(f.join("\t") + value.substring(end));
22435                 this.el.dom.setSelectionRange(start - 1, start - 1);
22436                 
22437             },
22438             
22439             "home" : function(e){
22440                 e.preventDefault();
22441                 
22442                 var curr = this.el.dom.selectionStart;
22443                 var lines = this.getValue().split("\n");
22444                 
22445                 if(!lines.length){
22446                     return;
22447                 }
22448                 
22449                 if(e.ctrlKey){
22450                     this.el.dom.setSelectionRange(0, 0);
22451                     return;
22452                 }
22453                 
22454                 var pos = 0;
22455                 
22456                 for (var i = 0; i < lines.length;i++) {
22457                     pos += lines[i].length;
22458                     
22459                     if(i != 0){
22460                         pos += 1;
22461                     }
22462                     
22463                     if(pos < curr){
22464                         continue;
22465                     }
22466                     
22467                     pos -= lines[i].length;
22468                     
22469                     break;
22470                 }
22471                 
22472                 if(!e.shiftKey){
22473                     this.el.dom.setSelectionRange(pos, pos);
22474                     return;
22475                 }
22476                 
22477                 this.el.dom.selectionStart = pos;
22478                 this.el.dom.selectionEnd = curr;
22479             },
22480             
22481             "end" : function(e){
22482                 e.preventDefault();
22483                 
22484                 var curr = this.el.dom.selectionStart;
22485                 var lines = this.getValue().split("\n");
22486                 
22487                 if(!lines.length){
22488                     return;
22489                 }
22490                 
22491                 if(e.ctrlKey){
22492                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22493                     return;
22494                 }
22495                 
22496                 var pos = 0;
22497                 
22498                 for (var i = 0; i < lines.length;i++) {
22499                     
22500                     pos += lines[i].length;
22501                     
22502                     if(i != 0){
22503                         pos += 1;
22504                     }
22505                     
22506                     if(pos < curr){
22507                         continue;
22508                     }
22509                     
22510                     break;
22511                 }
22512                 
22513                 if(!e.shiftKey){
22514                     this.el.dom.setSelectionRange(pos, pos);
22515                     return;
22516                 }
22517                 
22518                 this.el.dom.selectionStart = curr;
22519                 this.el.dom.selectionEnd = pos;
22520             },
22521
22522             scope : this,
22523
22524             doRelay : function(foo, bar, hname){
22525                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22526             },
22527
22528             forceKeyDown: true
22529         });
22530         
22531 //        if(this.autosave && this.w){
22532 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22533 //        }
22534     },
22535
22536     // private
22537     onResize : function(w, h)
22538     {
22539         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22540         var ew = false;
22541         var eh = false;
22542         
22543         if(this.el ){
22544             if(typeof w == 'number'){
22545                 var aw = w - this.wrap.getFrameWidth('lr');
22546                 this.el.setWidth(this.adjustWidth('textarea', aw));
22547                 ew = aw;
22548             }
22549             if(typeof h == 'number'){
22550                 var tbh = 0;
22551                 for (var i =0; i < this.toolbars.length;i++) {
22552                     // fixme - ask toolbars for heights?
22553                     tbh += this.toolbars[i].tb.el.getHeight();
22554                     if (this.toolbars[i].footer) {
22555                         tbh += this.toolbars[i].footer.el.getHeight();
22556                     }
22557                 }
22558                 
22559                 
22560                 
22561                 
22562                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22563                 ah -= 5; // knock a few pixes off for look..
22564 //                Roo.log(ah);
22565                 this.el.setHeight(this.adjustWidth('textarea', ah));
22566                 var eh = ah;
22567             }
22568         }
22569         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22570         this.editorcore.onResize(ew,eh);
22571         
22572     },
22573
22574     /**
22575      * Toggles the editor between standard and source edit mode.
22576      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22577      */
22578     toggleSourceEdit : function(sourceEditMode)
22579     {
22580         this.editorcore.toggleSourceEdit(sourceEditMode);
22581         
22582         if(this.editorcore.sourceEditMode){
22583             Roo.log('editor - showing textarea');
22584             
22585 //            Roo.log('in');
22586 //            Roo.log(this.syncValue());
22587             this.editorcore.syncValue();
22588             this.el.removeClass('x-hidden');
22589             this.el.dom.removeAttribute('tabIndex');
22590             this.el.focus();
22591             
22592             for (var i = 0; i < this.toolbars.length; i++) {
22593                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22594                     this.toolbars[i].tb.hide();
22595                     this.toolbars[i].footer.hide();
22596                 }
22597             }
22598             
22599         }else{
22600             Roo.log('editor - hiding textarea');
22601 //            Roo.log('out')
22602 //            Roo.log(this.pushValue()); 
22603             this.editorcore.pushValue();
22604             
22605             this.el.addClass('x-hidden');
22606             this.el.dom.setAttribute('tabIndex', -1);
22607             
22608             for (var i = 0; i < this.toolbars.length; i++) {
22609                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22610                     this.toolbars[i].tb.show();
22611                     this.toolbars[i].footer.show();
22612                 }
22613             }
22614             
22615             //this.deferFocus();
22616         }
22617         
22618         this.setSize(this.wrap.getSize());
22619         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22620         
22621         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22622     },
22623  
22624     // private (for BoxComponent)
22625     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22626
22627     // private (for BoxComponent)
22628     getResizeEl : function(){
22629         return this.wrap;
22630     },
22631
22632     // private (for BoxComponent)
22633     getPositionEl : function(){
22634         return this.wrap;
22635     },
22636
22637     // private
22638     initEvents : function(){
22639         this.originalValue = this.getValue();
22640     },
22641
22642     /**
22643      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22644      * @method
22645      */
22646     markInvalid : Roo.emptyFn,
22647     /**
22648      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22649      * @method
22650      */
22651     clearInvalid : Roo.emptyFn,
22652
22653     setValue : function(v){
22654         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22655         this.editorcore.pushValue();
22656     },
22657
22658      
22659     // private
22660     deferFocus : function(){
22661         this.focus.defer(10, this);
22662     },
22663
22664     // doc'ed in Field
22665     focus : function(){
22666         this.editorcore.focus();
22667         
22668     },
22669       
22670
22671     // private
22672     onDestroy : function(){
22673         
22674         
22675         
22676         if(this.rendered){
22677             
22678             for (var i =0; i < this.toolbars.length;i++) {
22679                 // fixme - ask toolbars for heights?
22680                 this.toolbars[i].onDestroy();
22681             }
22682             
22683             this.wrap.dom.innerHTML = '';
22684             this.wrap.remove();
22685         }
22686     },
22687
22688     // private
22689     onFirstFocus : function(){
22690         //Roo.log("onFirstFocus");
22691         this.editorcore.onFirstFocus();
22692          for (var i =0; i < this.toolbars.length;i++) {
22693             this.toolbars[i].onFirstFocus();
22694         }
22695         
22696     },
22697     
22698     // private
22699     syncValue : function()
22700     {
22701         this.editorcore.syncValue();
22702     },
22703     
22704     pushValue : function()
22705     {
22706         this.editorcore.pushValue();
22707     },
22708     
22709     setStylesheets : function(stylesheets)
22710     {
22711         this.editorcore.setStylesheets(stylesheets);
22712     },
22713     
22714     removeStylesheets : function()
22715     {
22716         this.editorcore.removeStylesheets();
22717     }
22718      
22719     
22720     // hide stuff that is not compatible
22721     /**
22722      * @event blur
22723      * @hide
22724      */
22725     /**
22726      * @event change
22727      * @hide
22728      */
22729     /**
22730      * @event focus
22731      * @hide
22732      */
22733     /**
22734      * @event specialkey
22735      * @hide
22736      */
22737     /**
22738      * @cfg {String} fieldClass @hide
22739      */
22740     /**
22741      * @cfg {String} focusClass @hide
22742      */
22743     /**
22744      * @cfg {String} autoCreate @hide
22745      */
22746     /**
22747      * @cfg {String} inputType @hide
22748      */
22749     /**
22750      * @cfg {String} invalidClass @hide
22751      */
22752     /**
22753      * @cfg {String} invalidText @hide
22754      */
22755     /**
22756      * @cfg {String} msgFx @hide
22757      */
22758     /**
22759      * @cfg {String} validateOnBlur @hide
22760      */
22761 });
22762  
22763     // <script type="text/javascript">
22764 /*
22765  * Based on
22766  * Ext JS Library 1.1.1
22767  * Copyright(c) 2006-2007, Ext JS, LLC.
22768  *  
22769  
22770  */
22771
22772 /**
22773  * @class Roo.form.HtmlEditorToolbar1
22774  * Basic Toolbar
22775  * 
22776  * Usage:
22777  *
22778  new Roo.form.HtmlEditor({
22779     ....
22780     toolbars : [
22781         new Roo.form.HtmlEditorToolbar1({
22782             disable : { fonts: 1 , format: 1, ..., ... , ...],
22783             btns : [ .... ]
22784         })
22785     }
22786      
22787  * 
22788  * @cfg {Object} disable List of elements to disable..
22789  * @cfg {Array} btns List of additional buttons.
22790  * 
22791  * 
22792  * NEEDS Extra CSS? 
22793  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22794  */
22795  
22796 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22797 {
22798     
22799     Roo.apply(this, config);
22800     
22801     // default disabled, based on 'good practice'..
22802     this.disable = this.disable || {};
22803     Roo.applyIf(this.disable, {
22804         fontSize : true,
22805         colors : true,
22806         specialElements : true
22807     });
22808     
22809     
22810     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22811     // dont call parent... till later.
22812 }
22813
22814 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22815     
22816     tb: false,
22817     
22818     rendered: false,
22819     
22820     editor : false,
22821     editorcore : false,
22822     /**
22823      * @cfg {Object} disable  List of toolbar elements to disable
22824          
22825      */
22826     disable : false,
22827     
22828     
22829      /**
22830      * @cfg {String} createLinkText The default text for the create link prompt
22831      */
22832     createLinkText : 'Please enter the URL for the link:',
22833     /**
22834      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22835      */
22836     defaultLinkValue : 'http:/'+'/',
22837    
22838     
22839       /**
22840      * @cfg {Array} fontFamilies An array of available font families
22841      */
22842     fontFamilies : [
22843         'Arial',
22844         'Courier New',
22845         'Tahoma',
22846         'Times New Roman',
22847         'Verdana'
22848     ],
22849     
22850     specialChars : [
22851            "&#169;",
22852           "&#174;",     
22853           "&#8482;",    
22854           "&#163;" ,    
22855          // "&#8212;",    
22856           "&#8230;",    
22857           "&#247;" ,    
22858         //  "&#225;" ,     ?? a acute?
22859            "&#8364;"    , //Euro
22860        //   "&#8220;"    ,
22861         //  "&#8221;"    ,
22862         //  "&#8226;"    ,
22863           "&#176;"  //   , // degrees
22864
22865          // "&#233;"     , // e ecute
22866          // "&#250;"     , // u ecute?
22867     ],
22868     
22869     specialElements : [
22870         {
22871             text: "Insert Table",
22872             xtype: 'MenuItem',
22873             xns : Roo.Menu,
22874             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
22875                 
22876         },
22877         {    
22878             text: "Insert Image",
22879             xtype: 'MenuItem',
22880             xns : Roo.Menu,
22881             ihtml : '<img src="about:blank"/>'
22882             
22883         }
22884         
22885          
22886     ],
22887     
22888     
22889     inputElements : [ 
22890             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
22891             "input:submit", "input:button", "select", "textarea", "label" ],
22892     formats : [
22893         ["p"] ,  
22894         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
22895         ["pre"],[ "code"], 
22896         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22897         ['div'],['span']
22898     ],
22899     
22900     cleanStyles : [
22901         "font-size"
22902     ],
22903      /**
22904      * @cfg {String} defaultFont default font to use.
22905      */
22906     defaultFont: 'tahoma',
22907    
22908     fontSelect : false,
22909     
22910     
22911     formatCombo : false,
22912     
22913     init : function(editor)
22914     {
22915         this.editor = editor;
22916         this.editorcore = editor.editorcore ? editor.editorcore : editor;
22917         var editorcore = this.editorcore;
22918         
22919         var _t = this;
22920         
22921         var fid = editorcore.frameId;
22922         var etb = this;
22923         function btn(id, toggle, handler){
22924             var xid = fid + '-'+ id ;
22925             return {
22926                 id : xid,
22927                 cmd : id,
22928                 cls : 'x-btn-icon x-edit-'+id,
22929                 enableToggle:toggle !== false,
22930                 scope: _t, // was editor...
22931                 handler:handler||_t.relayBtnCmd,
22932                 clickEvent:'mousedown',
22933                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
22934                 tabIndex:-1
22935             };
22936         }
22937         
22938         
22939         
22940         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
22941         this.tb = tb;
22942          // stop form submits
22943         tb.el.on('click', function(e){
22944             e.preventDefault(); // what does this do?
22945         });
22946
22947         if(!this.disable.font) { // && !Roo.isSafari){
22948             /* why no safari for fonts 
22949             editor.fontSelect = tb.el.createChild({
22950                 tag:'select',
22951                 tabIndex: -1,
22952                 cls:'x-font-select',
22953                 html: this.createFontOptions()
22954             });
22955             
22956             editor.fontSelect.on('change', function(){
22957                 var font = editor.fontSelect.dom.value;
22958                 editor.relayCmd('fontname', font);
22959                 editor.deferFocus();
22960             }, editor);
22961             
22962             tb.add(
22963                 editor.fontSelect.dom,
22964                 '-'
22965             );
22966             */
22967             
22968         };
22969         if(!this.disable.formats){
22970             this.formatCombo = new Roo.form.ComboBox({
22971                 store: new Roo.data.SimpleStore({
22972                     id : 'tag',
22973                     fields: ['tag'],
22974                     data : this.formats // from states.js
22975                 }),
22976                 blockFocus : true,
22977                 name : '',
22978                 //autoCreate : {tag: "div",  size: "20"},
22979                 displayField:'tag',
22980                 typeAhead: false,
22981                 mode: 'local',
22982                 editable : false,
22983                 triggerAction: 'all',
22984                 emptyText:'Add tag',
22985                 selectOnFocus:true,
22986                 width:135,
22987                 listeners : {
22988                     'select': function(c, r, i) {
22989                         editorcore.insertTag(r.get('tag'));
22990                         editor.focus();
22991                     }
22992                 }
22993
22994             });
22995             tb.addField(this.formatCombo);
22996             
22997         }
22998         
22999         if(!this.disable.format){
23000             tb.add(
23001                 btn('bold'),
23002                 btn('italic'),
23003                 btn('underline'),
23004                 btn('strikethrough')
23005             );
23006         };
23007         if(!this.disable.fontSize){
23008             tb.add(
23009                 '-',
23010                 
23011                 
23012                 btn('increasefontsize', false, editorcore.adjustFont),
23013                 btn('decreasefontsize', false, editorcore.adjustFont)
23014             );
23015         };
23016         
23017         
23018         if(!this.disable.colors){
23019             tb.add(
23020                 '-', {
23021                     id:editorcore.frameId +'-forecolor',
23022                     cls:'x-btn-icon x-edit-forecolor',
23023                     clickEvent:'mousedown',
23024                     tooltip: this.buttonTips['forecolor'] || undefined,
23025                     tabIndex:-1,
23026                     menu : new Roo.menu.ColorMenu({
23027                         allowReselect: true,
23028                         focus: Roo.emptyFn,
23029                         value:'000000',
23030                         plain:true,
23031                         selectHandler: function(cp, color){
23032                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23033                             editor.deferFocus();
23034                         },
23035                         scope: editorcore,
23036                         clickEvent:'mousedown'
23037                     })
23038                 }, {
23039                     id:editorcore.frameId +'backcolor',
23040                     cls:'x-btn-icon x-edit-backcolor',
23041                     clickEvent:'mousedown',
23042                     tooltip: this.buttonTips['backcolor'] || undefined,
23043                     tabIndex:-1,
23044                     menu : new Roo.menu.ColorMenu({
23045                         focus: Roo.emptyFn,
23046                         value:'FFFFFF',
23047                         plain:true,
23048                         allowReselect: true,
23049                         selectHandler: function(cp, color){
23050                             if(Roo.isGecko){
23051                                 editorcore.execCmd('useCSS', false);
23052                                 editorcore.execCmd('hilitecolor', color);
23053                                 editorcore.execCmd('useCSS', true);
23054                                 editor.deferFocus();
23055                             }else{
23056                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23057                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23058                                 editor.deferFocus();
23059                             }
23060                         },
23061                         scope:editorcore,
23062                         clickEvent:'mousedown'
23063                     })
23064                 }
23065             );
23066         };
23067         // now add all the items...
23068         
23069
23070         if(!this.disable.alignments){
23071             tb.add(
23072                 '-',
23073                 btn('justifyleft'),
23074                 btn('justifycenter'),
23075                 btn('justifyright')
23076             );
23077         };
23078
23079         //if(!Roo.isSafari){
23080             if(!this.disable.links){
23081                 tb.add(
23082                     '-',
23083                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23084                 );
23085             };
23086
23087             if(!this.disable.lists){
23088                 tb.add(
23089                     '-',
23090                     btn('insertorderedlist'),
23091                     btn('insertunorderedlist')
23092                 );
23093             }
23094             if(!this.disable.sourceEdit){
23095                 tb.add(
23096                     '-',
23097                     btn('sourceedit', true, function(btn){
23098                         this.toggleSourceEdit(btn.pressed);
23099                     })
23100                 );
23101             }
23102         //}
23103         
23104         var smenu = { };
23105         // special menu.. - needs to be tidied up..
23106         if (!this.disable.special) {
23107             smenu = {
23108                 text: "&#169;",
23109                 cls: 'x-edit-none',
23110                 
23111                 menu : {
23112                     items : []
23113                 }
23114             };
23115             for (var i =0; i < this.specialChars.length; i++) {
23116                 smenu.menu.items.push({
23117                     
23118                     html: this.specialChars[i],
23119                     handler: function(a,b) {
23120                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23121                         //editor.insertAtCursor(a.html);
23122                         
23123                     },
23124                     tabIndex:-1
23125                 });
23126             }
23127             
23128             
23129             tb.add(smenu);
23130             
23131             
23132         }
23133         
23134         var cmenu = { };
23135         if (!this.disable.cleanStyles) {
23136             cmenu = {
23137                 cls: 'x-btn-icon x-btn-clear',
23138                 
23139                 menu : {
23140                     items : []
23141                 }
23142             };
23143             for (var i =0; i < this.cleanStyles.length; i++) {
23144                 cmenu.menu.items.push({
23145                     actiontype : this.cleanStyles[i],
23146                     html: 'Remove ' + this.cleanStyles[i],
23147                     handler: function(a,b) {
23148 //                        Roo.log(a);
23149 //                        Roo.log(b);
23150                         var c = Roo.get(editorcore.doc.body);
23151                         c.select('[style]').each(function(s) {
23152                             s.dom.style.removeProperty(a.actiontype);
23153                         });
23154                         editorcore.syncValue();
23155                     },
23156                     tabIndex:-1
23157                 });
23158             }
23159              cmenu.menu.items.push({
23160                 actiontype : 'tablewidths',
23161                 html: 'Remove Table Widths',
23162                 handler: function(a,b) {
23163                     editorcore.cleanTableWidths();
23164                     editorcore.syncValue();
23165                 },
23166                 tabIndex:-1
23167             });
23168             cmenu.menu.items.push({
23169                 actiontype : 'word',
23170                 html: 'Remove MS Word Formating',
23171                 handler: function(a,b) {
23172                     editorcore.cleanWord();
23173                     editorcore.syncValue();
23174                 },
23175                 tabIndex:-1
23176             });
23177             
23178             cmenu.menu.items.push({
23179                 actiontype : 'all',
23180                 html: 'Remove All Styles',
23181                 handler: function(a,b) {
23182                     
23183                     var c = Roo.get(editorcore.doc.body);
23184                     c.select('[style]').each(function(s) {
23185                         s.dom.removeAttribute('style');
23186                     });
23187                     editorcore.syncValue();
23188                 },
23189                 tabIndex:-1
23190             });
23191             
23192             cmenu.menu.items.push({
23193                 actiontype : 'all',
23194                 html: 'Remove All CSS Classes',
23195                 handler: function(a,b) {
23196                     
23197                     var c = Roo.get(editorcore.doc.body);
23198                     c.select('[class]').each(function(s) {
23199                         s.dom.className = '';
23200                     });
23201                     editorcore.syncValue();
23202                 },
23203                 tabIndex:-1
23204             });
23205             
23206              cmenu.menu.items.push({
23207                 actiontype : 'tidy',
23208                 html: 'Tidy HTML Source',
23209                 handler: function(a,b) {
23210                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23211                     editorcore.syncValue();
23212                 },
23213                 tabIndex:-1
23214             });
23215             
23216             
23217             tb.add(cmenu);
23218         }
23219          
23220         if (!this.disable.specialElements) {
23221             var semenu = {
23222                 text: "Other;",
23223                 cls: 'x-edit-none',
23224                 menu : {
23225                     items : []
23226                 }
23227             };
23228             for (var i =0; i < this.specialElements.length; i++) {
23229                 semenu.menu.items.push(
23230                     Roo.apply({ 
23231                         handler: function(a,b) {
23232                             editor.insertAtCursor(this.ihtml);
23233                         }
23234                     }, this.specialElements[i])
23235                 );
23236                     
23237             }
23238             
23239             tb.add(semenu);
23240             
23241             
23242         }
23243          
23244         
23245         if (this.btns) {
23246             for(var i =0; i< this.btns.length;i++) {
23247                 var b = Roo.factory(this.btns[i],Roo.form);
23248                 b.cls =  'x-edit-none';
23249                 
23250                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23251                     b.cls += ' x-init-enable';
23252                 }
23253                 
23254                 b.scope = editorcore;
23255                 tb.add(b);
23256             }
23257         
23258         }
23259         
23260         
23261         
23262         // disable everything...
23263         
23264         this.tb.items.each(function(item){
23265             
23266            if(
23267                 item.id != editorcore.frameId+ '-sourceedit' && 
23268                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23269             ){
23270                 
23271                 item.disable();
23272             }
23273         });
23274         this.rendered = true;
23275         
23276         // the all the btns;
23277         editor.on('editorevent', this.updateToolbar, this);
23278         // other toolbars need to implement this..
23279         //editor.on('editmodechange', this.updateToolbar, this);
23280     },
23281     
23282     
23283     relayBtnCmd : function(btn) {
23284         this.editorcore.relayCmd(btn.cmd);
23285     },
23286     // private used internally
23287     createLink : function(){
23288         Roo.log("create link?");
23289         var url = prompt(this.createLinkText, this.defaultLinkValue);
23290         if(url && url != 'http:/'+'/'){
23291             this.editorcore.relayCmd('createlink', url);
23292         }
23293     },
23294
23295     
23296     /**
23297      * Protected method that will not generally be called directly. It triggers
23298      * a toolbar update by reading the markup state of the current selection in the editor.
23299      */
23300     updateToolbar: function(){
23301
23302         if(!this.editorcore.activated){
23303             this.editor.onFirstFocus();
23304             return;
23305         }
23306
23307         var btns = this.tb.items.map, 
23308             doc = this.editorcore.doc,
23309             frameId = this.editorcore.frameId;
23310
23311         if(!this.disable.font && !Roo.isSafari){
23312             /*
23313             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23314             if(name != this.fontSelect.dom.value){
23315                 this.fontSelect.dom.value = name;
23316             }
23317             */
23318         }
23319         if(!this.disable.format){
23320             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23321             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23322             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23323             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23324         }
23325         if(!this.disable.alignments){
23326             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23327             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23328             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23329         }
23330         if(!Roo.isSafari && !this.disable.lists){
23331             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23332             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23333         }
23334         
23335         var ans = this.editorcore.getAllAncestors();
23336         if (this.formatCombo) {
23337             
23338             
23339             var store = this.formatCombo.store;
23340             this.formatCombo.setValue("");
23341             for (var i =0; i < ans.length;i++) {
23342                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23343                     // select it..
23344                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23345                     break;
23346                 }
23347             }
23348         }
23349         
23350         
23351         
23352         // hides menus... - so this cant be on a menu...
23353         Roo.menu.MenuMgr.hideAll();
23354
23355         //this.editorsyncValue();
23356     },
23357    
23358     
23359     createFontOptions : function(){
23360         var buf = [], fs = this.fontFamilies, ff, lc;
23361         
23362         
23363         
23364         for(var i = 0, len = fs.length; i< len; i++){
23365             ff = fs[i];
23366             lc = ff.toLowerCase();
23367             buf.push(
23368                 '<option value="',lc,'" style="font-family:',ff,';"',
23369                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23370                     ff,
23371                 '</option>'
23372             );
23373         }
23374         return buf.join('');
23375     },
23376     
23377     toggleSourceEdit : function(sourceEditMode){
23378         
23379         Roo.log("toolbar toogle");
23380         if(sourceEditMode === undefined){
23381             sourceEditMode = !this.sourceEditMode;
23382         }
23383         this.sourceEditMode = sourceEditMode === true;
23384         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23385         // just toggle the button?
23386         if(btn.pressed !== this.sourceEditMode){
23387             btn.toggle(this.sourceEditMode);
23388             return;
23389         }
23390         
23391         if(sourceEditMode){
23392             Roo.log("disabling buttons");
23393             this.tb.items.each(function(item){
23394                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23395                     item.disable();
23396                 }
23397             });
23398           
23399         }else{
23400             Roo.log("enabling buttons");
23401             if(this.editorcore.initialized){
23402                 this.tb.items.each(function(item){
23403                     item.enable();
23404                 });
23405             }
23406             
23407         }
23408         Roo.log("calling toggole on editor");
23409         // tell the editor that it's been pressed..
23410         this.editor.toggleSourceEdit(sourceEditMode);
23411        
23412     },
23413      /**
23414      * Object collection of toolbar tooltips for the buttons in the editor. The key
23415      * is the command id associated with that button and the value is a valid QuickTips object.
23416      * For example:
23417 <pre><code>
23418 {
23419     bold : {
23420         title: 'Bold (Ctrl+B)',
23421         text: 'Make the selected text bold.',
23422         cls: 'x-html-editor-tip'
23423     },
23424     italic : {
23425         title: 'Italic (Ctrl+I)',
23426         text: 'Make the selected text italic.',
23427         cls: 'x-html-editor-tip'
23428     },
23429     ...
23430 </code></pre>
23431     * @type Object
23432      */
23433     buttonTips : {
23434         bold : {
23435             title: 'Bold (Ctrl+B)',
23436             text: 'Make the selected text bold.',
23437             cls: 'x-html-editor-tip'
23438         },
23439         italic : {
23440             title: 'Italic (Ctrl+I)',
23441             text: 'Make the selected text italic.',
23442             cls: 'x-html-editor-tip'
23443         },
23444         underline : {
23445             title: 'Underline (Ctrl+U)',
23446             text: 'Underline the selected text.',
23447             cls: 'x-html-editor-tip'
23448         },
23449         strikethrough : {
23450             title: 'Strikethrough',
23451             text: 'Strikethrough the selected text.',
23452             cls: 'x-html-editor-tip'
23453         },
23454         increasefontsize : {
23455             title: 'Grow Text',
23456             text: 'Increase the font size.',
23457             cls: 'x-html-editor-tip'
23458         },
23459         decreasefontsize : {
23460             title: 'Shrink Text',
23461             text: 'Decrease the font size.',
23462             cls: 'x-html-editor-tip'
23463         },
23464         backcolor : {
23465             title: 'Text Highlight Color',
23466             text: 'Change the background color of the selected text.',
23467             cls: 'x-html-editor-tip'
23468         },
23469         forecolor : {
23470             title: 'Font Color',
23471             text: 'Change the color of the selected text.',
23472             cls: 'x-html-editor-tip'
23473         },
23474         justifyleft : {
23475             title: 'Align Text Left',
23476             text: 'Align text to the left.',
23477             cls: 'x-html-editor-tip'
23478         },
23479         justifycenter : {
23480             title: 'Center Text',
23481             text: 'Center text in the editor.',
23482             cls: 'x-html-editor-tip'
23483         },
23484         justifyright : {
23485             title: 'Align Text Right',
23486             text: 'Align text to the right.',
23487             cls: 'x-html-editor-tip'
23488         },
23489         insertunorderedlist : {
23490             title: 'Bullet List',
23491             text: 'Start a bulleted list.',
23492             cls: 'x-html-editor-tip'
23493         },
23494         insertorderedlist : {
23495             title: 'Numbered List',
23496             text: 'Start a numbered list.',
23497             cls: 'x-html-editor-tip'
23498         },
23499         createlink : {
23500             title: 'Hyperlink',
23501             text: 'Make the selected text a hyperlink.',
23502             cls: 'x-html-editor-tip'
23503         },
23504         sourceedit : {
23505             title: 'Source Edit',
23506             text: 'Switch to source editing mode.',
23507             cls: 'x-html-editor-tip'
23508         }
23509     },
23510     // private
23511     onDestroy : function(){
23512         if(this.rendered){
23513             
23514             this.tb.items.each(function(item){
23515                 if(item.menu){
23516                     item.menu.removeAll();
23517                     if(item.menu.el){
23518                         item.menu.el.destroy();
23519                     }
23520                 }
23521                 item.destroy();
23522             });
23523              
23524         }
23525     },
23526     onFirstFocus: function() {
23527         this.tb.items.each(function(item){
23528            item.enable();
23529         });
23530     }
23531 });
23532
23533
23534
23535
23536 // <script type="text/javascript">
23537 /*
23538  * Based on
23539  * Ext JS Library 1.1.1
23540  * Copyright(c) 2006-2007, Ext JS, LLC.
23541  *  
23542  
23543  */
23544
23545  
23546 /**
23547  * @class Roo.form.HtmlEditor.ToolbarContext
23548  * Context Toolbar
23549  * 
23550  * Usage:
23551  *
23552  new Roo.form.HtmlEditor({
23553     ....
23554     toolbars : [
23555         { xtype: 'ToolbarStandard', styles : {} }
23556         { xtype: 'ToolbarContext', disable : {} }
23557     ]
23558 })
23559
23560      
23561  * 
23562  * @config : {Object} disable List of elements to disable.. (not done yet.)
23563  * @config : {Object} styles  Map of styles available.
23564  * 
23565  */
23566
23567 Roo.form.HtmlEditor.ToolbarContext = function(config)
23568 {
23569     
23570     Roo.apply(this, config);
23571     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23572     // dont call parent... till later.
23573     this.styles = this.styles || {};
23574 }
23575
23576  
23577
23578 Roo.form.HtmlEditor.ToolbarContext.types = {
23579     'IMG' : {
23580         width : {
23581             title: "Width",
23582             width: 40
23583         },
23584         height:  {
23585             title: "Height",
23586             width: 40
23587         },
23588         align: {
23589             title: "Align",
23590             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23591             width : 80
23592             
23593         },
23594         border: {
23595             title: "Border",
23596             width: 40
23597         },
23598         alt: {
23599             title: "Alt",
23600             width: 120
23601         },
23602         src : {
23603             title: "Src",
23604             width: 220
23605         }
23606         
23607     },
23608     'A' : {
23609         name : {
23610             title: "Name",
23611             width: 50
23612         },
23613         target:  {
23614             title: "Target",
23615             width: 120
23616         },
23617         href:  {
23618             title: "Href",
23619             width: 220
23620         } // border?
23621         
23622     },
23623     'TABLE' : {
23624         rows : {
23625             title: "Rows",
23626             width: 20
23627         },
23628         cols : {
23629             title: "Cols",
23630             width: 20
23631         },
23632         width : {
23633             title: "Width",
23634             width: 40
23635         },
23636         height : {
23637             title: "Height",
23638             width: 40
23639         },
23640         border : {
23641             title: "Border",
23642             width: 20
23643         }
23644     },
23645     'TD' : {
23646         width : {
23647             title: "Width",
23648             width: 40
23649         },
23650         height : {
23651             title: "Height",
23652             width: 40
23653         },   
23654         align: {
23655             title: "Align",
23656             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23657             width: 80
23658         },
23659         valign: {
23660             title: "Valign",
23661             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23662             width: 80
23663         },
23664         colspan: {
23665             title: "Colspan",
23666             width: 20
23667             
23668         },
23669          'font-family'  : {
23670             title : "Font",
23671             style : 'fontFamily',
23672             displayField: 'display',
23673             optname : 'font-family',
23674             width: 140
23675         }
23676     },
23677     'INPUT' : {
23678         name : {
23679             title: "name",
23680             width: 120
23681         },
23682         value : {
23683             title: "Value",
23684             width: 120
23685         },
23686         width : {
23687             title: "Width",
23688             width: 40
23689         }
23690     },
23691     'LABEL' : {
23692         'for' : {
23693             title: "For",
23694             width: 120
23695         }
23696     },
23697     'TEXTAREA' : {
23698           name : {
23699             title: "name",
23700             width: 120
23701         },
23702         rows : {
23703             title: "Rows",
23704             width: 20
23705         },
23706         cols : {
23707             title: "Cols",
23708             width: 20
23709         }
23710     },
23711     'SELECT' : {
23712         name : {
23713             title: "name",
23714             width: 120
23715         },
23716         selectoptions : {
23717             title: "Options",
23718             width: 200
23719         }
23720     },
23721     
23722     // should we really allow this??
23723     // should this just be 
23724     'BODY' : {
23725         title : {
23726             title: "Title",
23727             width: 200,
23728             disabled : true
23729         }
23730     },
23731     'SPAN' : {
23732         'font-family'  : {
23733             title : "Font",
23734             style : 'fontFamily',
23735             displayField: 'display',
23736             optname : 'font-family',
23737             width: 140
23738         }
23739     },
23740     'DIV' : {
23741         'font-family'  : {
23742             title : "Font",
23743             style : 'fontFamily',
23744             displayField: 'display',
23745             optname : 'font-family',
23746             width: 140
23747         }
23748     },
23749      'P' : {
23750         'font-family'  : {
23751             title : "Font",
23752             style : 'fontFamily',
23753             displayField: 'display',
23754             optname : 'font-family',
23755             width: 140
23756         }
23757     },
23758     
23759     '*' : {
23760         // empty..
23761     }
23762
23763 };
23764
23765 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23766 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23767
23768 Roo.form.HtmlEditor.ToolbarContext.options = {
23769         'font-family'  : [ 
23770                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23771                 [ 'Courier New', 'Courier New'],
23772                 [ 'Tahoma', 'Tahoma'],
23773                 [ 'Times New Roman,serif', 'Times'],
23774                 [ 'Verdana','Verdana' ]
23775         ]
23776 };
23777
23778 // fixme - these need to be configurable..
23779  
23780
23781 //Roo.form.HtmlEditor.ToolbarContext.types
23782
23783
23784 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23785     
23786     tb: false,
23787     
23788     rendered: false,
23789     
23790     editor : false,
23791     editorcore : false,
23792     /**
23793      * @cfg {Object} disable  List of toolbar elements to disable
23794          
23795      */
23796     disable : false,
23797     /**
23798      * @cfg {Object} styles List of styles 
23799      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23800      *
23801      * These must be defined in the page, so they get rendered correctly..
23802      * .headline { }
23803      * TD.underline { }
23804      * 
23805      */
23806     styles : false,
23807     
23808     options: false,
23809     
23810     toolbars : false,
23811     
23812     init : function(editor)
23813     {
23814         this.editor = editor;
23815         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23816         var editorcore = this.editorcore;
23817         
23818         var fid = editorcore.frameId;
23819         var etb = this;
23820         function btn(id, toggle, handler){
23821             var xid = fid + '-'+ id ;
23822             return {
23823                 id : xid,
23824                 cmd : id,
23825                 cls : 'x-btn-icon x-edit-'+id,
23826                 enableToggle:toggle !== false,
23827                 scope: editorcore, // was editor...
23828                 handler:handler||editorcore.relayBtnCmd,
23829                 clickEvent:'mousedown',
23830                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23831                 tabIndex:-1
23832             };
23833         }
23834         // create a new element.
23835         var wdiv = editor.wrap.createChild({
23836                 tag: 'div'
23837             }, editor.wrap.dom.firstChild.nextSibling, true);
23838         
23839         // can we do this more than once??
23840         
23841          // stop form submits
23842       
23843  
23844         // disable everything...
23845         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23846         this.toolbars = {};
23847            
23848         for (var i in  ty) {
23849           
23850             this.toolbars[i] = this.buildToolbar(ty[i],i);
23851         }
23852         this.tb = this.toolbars.BODY;
23853         this.tb.el.show();
23854         this.buildFooter();
23855         this.footer.show();
23856         editor.on('hide', function( ) { this.footer.hide() }, this);
23857         editor.on('show', function( ) { this.footer.show() }, this);
23858         
23859          
23860         this.rendered = true;
23861         
23862         // the all the btns;
23863         editor.on('editorevent', this.updateToolbar, this);
23864         // other toolbars need to implement this..
23865         //editor.on('editmodechange', this.updateToolbar, this);
23866     },
23867     
23868     
23869     
23870     /**
23871      * Protected method that will not generally be called directly. It triggers
23872      * a toolbar update by reading the markup state of the current selection in the editor.
23873      *
23874      * Note you can force an update by calling on('editorevent', scope, false)
23875      */
23876     updateToolbar: function(editor,ev,sel){
23877
23878         //Roo.log(ev);
23879         // capture mouse up - this is handy for selecting images..
23880         // perhaps should go somewhere else...
23881         if(!this.editorcore.activated){
23882              this.editor.onFirstFocus();
23883             return;
23884         }
23885         
23886         
23887         
23888         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23889         // selectNode - might want to handle IE?
23890         if (ev &&
23891             (ev.type == 'mouseup' || ev.type == 'click' ) &&
23892             ev.target && ev.target.tagName == 'IMG') {
23893             // they have click on an image...
23894             // let's see if we can change the selection...
23895             sel = ev.target;
23896          
23897               var nodeRange = sel.ownerDocument.createRange();
23898             try {
23899                 nodeRange.selectNode(sel);
23900             } catch (e) {
23901                 nodeRange.selectNodeContents(sel);
23902             }
23903             //nodeRange.collapse(true);
23904             var s = this.editorcore.win.getSelection();
23905             s.removeAllRanges();
23906             s.addRange(nodeRange);
23907         }  
23908         
23909       
23910         var updateFooter = sel ? false : true;
23911         
23912         
23913         var ans = this.editorcore.getAllAncestors();
23914         
23915         // pick
23916         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23917         
23918         if (!sel) { 
23919             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
23920             sel = sel ? sel : this.editorcore.doc.body;
23921             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
23922             
23923         }
23924         // pick a menu that exists..
23925         var tn = sel.tagName.toUpperCase();
23926         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
23927         
23928         tn = sel.tagName.toUpperCase();
23929         
23930         var lastSel = this.tb.selectedNode;
23931         
23932         this.tb.selectedNode = sel;
23933         
23934         // if current menu does not match..
23935         
23936         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
23937                 
23938             this.tb.el.hide();
23939             ///console.log("show: " + tn);
23940             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
23941             this.tb.el.show();
23942             // update name
23943             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
23944             
23945             
23946             // update attributes
23947             if (this.tb.fields) {
23948                 this.tb.fields.each(function(e) {
23949                     if (e.stylename) {
23950                         e.setValue(sel.style[e.stylename]);
23951                         return;
23952                     } 
23953                    e.setValue(sel.getAttribute(e.attrname));
23954                 });
23955             }
23956             
23957             var hasStyles = false;
23958             for(var i in this.styles) {
23959                 hasStyles = true;
23960                 break;
23961             }
23962             
23963             // update styles
23964             if (hasStyles) { 
23965                 var st = this.tb.fields.item(0);
23966                 
23967                 st.store.removeAll();
23968                
23969                 
23970                 var cn = sel.className.split(/\s+/);
23971                 
23972                 var avs = [];
23973                 if (this.styles['*']) {
23974                     
23975                     Roo.each(this.styles['*'], function(v) {
23976                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23977                     });
23978                 }
23979                 if (this.styles[tn]) { 
23980                     Roo.each(this.styles[tn], function(v) {
23981                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23982                     });
23983                 }
23984                 
23985                 st.store.loadData(avs);
23986                 st.collapse();
23987                 st.setValue(cn);
23988             }
23989             // flag our selected Node.
23990             this.tb.selectedNode = sel;
23991            
23992            
23993             Roo.menu.MenuMgr.hideAll();
23994
23995         }
23996         
23997         if (!updateFooter) {
23998             //this.footDisp.dom.innerHTML = ''; 
23999             return;
24000         }
24001         // update the footer
24002         //
24003         var html = '';
24004         
24005         this.footerEls = ans.reverse();
24006         Roo.each(this.footerEls, function(a,i) {
24007             if (!a) { return; }
24008             html += html.length ? ' &gt; '  :  '';
24009             
24010             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24011             
24012         });
24013        
24014         // 
24015         var sz = this.footDisp.up('td').getSize();
24016         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24017         this.footDisp.dom.style.marginLeft = '5px';
24018         
24019         this.footDisp.dom.style.overflow = 'hidden';
24020         
24021         this.footDisp.dom.innerHTML = html;
24022             
24023         //this.editorsyncValue();
24024     },
24025      
24026     
24027    
24028        
24029     // private
24030     onDestroy : function(){
24031         if(this.rendered){
24032             
24033             this.tb.items.each(function(item){
24034                 if(item.menu){
24035                     item.menu.removeAll();
24036                     if(item.menu.el){
24037                         item.menu.el.destroy();
24038                     }
24039                 }
24040                 item.destroy();
24041             });
24042              
24043         }
24044     },
24045     onFirstFocus: function() {
24046         // need to do this for all the toolbars..
24047         this.tb.items.each(function(item){
24048            item.enable();
24049         });
24050     },
24051     buildToolbar: function(tlist, nm)
24052     {
24053         var editor = this.editor;
24054         var editorcore = this.editorcore;
24055          // create a new element.
24056         var wdiv = editor.wrap.createChild({
24057                 tag: 'div'
24058             }, editor.wrap.dom.firstChild.nextSibling, true);
24059         
24060        
24061         var tb = new Roo.Toolbar(wdiv);
24062         // add the name..
24063         
24064         tb.add(nm+ ":&nbsp;");
24065         
24066         var styles = [];
24067         for(var i in this.styles) {
24068             styles.push(i);
24069         }
24070         
24071         // styles...
24072         if (styles && styles.length) {
24073             
24074             // this needs a multi-select checkbox...
24075             tb.addField( new Roo.form.ComboBox({
24076                 store: new Roo.data.SimpleStore({
24077                     id : 'val',
24078                     fields: ['val', 'selected'],
24079                     data : [] 
24080                 }),
24081                 name : '-roo-edit-className',
24082                 attrname : 'className',
24083                 displayField: 'val',
24084                 typeAhead: false,
24085                 mode: 'local',
24086                 editable : false,
24087                 triggerAction: 'all',
24088                 emptyText:'Select Style',
24089                 selectOnFocus:true,
24090                 width: 130,
24091                 listeners : {
24092                     'select': function(c, r, i) {
24093                         // initial support only for on class per el..
24094                         tb.selectedNode.className =  r ? r.get('val') : '';
24095                         editorcore.syncValue();
24096                     }
24097                 }
24098     
24099             }));
24100         }
24101         
24102         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24103         var tbops = tbc.options;
24104         
24105         for (var i in tlist) {
24106             
24107             var item = tlist[i];
24108             tb.add(item.title + ":&nbsp;");
24109             
24110             
24111             //optname == used so you can configure the options available..
24112             var opts = item.opts ? item.opts : false;
24113             if (item.optname) {
24114                 opts = tbops[item.optname];
24115            
24116             }
24117             
24118             if (opts) {
24119                 // opts == pulldown..
24120                 tb.addField( new Roo.form.ComboBox({
24121                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24122                         id : 'val',
24123                         fields: ['val', 'display'],
24124                         data : opts  
24125                     }),
24126                     name : '-roo-edit-' + i,
24127                     attrname : i,
24128                     stylename : item.style ? item.style : false,
24129                     displayField: item.displayField ? item.displayField : 'val',
24130                     valueField :  'val',
24131                     typeAhead: false,
24132                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24133                     editable : false,
24134                     triggerAction: 'all',
24135                     emptyText:'Select',
24136                     selectOnFocus:true,
24137                     width: item.width ? item.width  : 130,
24138                     listeners : {
24139                         'select': function(c, r, i) {
24140                             if (c.stylename) {
24141                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24142                                 return;
24143                             }
24144                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24145                         }
24146                     }
24147
24148                 }));
24149                 continue;
24150                     
24151                  
24152                 
24153                 tb.addField( new Roo.form.TextField({
24154                     name: i,
24155                     width: 100,
24156                     //allowBlank:false,
24157                     value: ''
24158                 }));
24159                 continue;
24160             }
24161             tb.addField( new Roo.form.TextField({
24162                 name: '-roo-edit-' + i,
24163                 attrname : i,
24164                 
24165                 width: item.width,
24166                 //allowBlank:true,
24167                 value: '',
24168                 listeners: {
24169                     'change' : function(f, nv, ov) {
24170                         tb.selectedNode.setAttribute(f.attrname, nv);
24171                         editorcore.syncValue();
24172                     }
24173                 }
24174             }));
24175              
24176         }
24177         
24178         var _this = this;
24179         
24180         if(nm == 'BODY'){
24181             tb.addSeparator();
24182         
24183             tb.addButton( {
24184                 text: 'Stylesheets',
24185
24186                 listeners : {
24187                     click : function ()
24188                     {
24189                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24190                     }
24191                 }
24192             });
24193         }
24194         
24195         tb.addFill();
24196         tb.addButton( {
24197             text: 'Remove Tag',
24198     
24199             listeners : {
24200                 click : function ()
24201                 {
24202                     // remove
24203                     // undo does not work.
24204                      
24205                     var sn = tb.selectedNode;
24206                     
24207                     var pn = sn.parentNode;
24208                     
24209                     var stn =  sn.childNodes[0];
24210                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24211                     while (sn.childNodes.length) {
24212                         var node = sn.childNodes[0];
24213                         sn.removeChild(node);
24214                         //Roo.log(node);
24215                         pn.insertBefore(node, sn);
24216                         
24217                     }
24218                     pn.removeChild(sn);
24219                     var range = editorcore.createRange();
24220         
24221                     range.setStart(stn,0);
24222                     range.setEnd(en,0); //????
24223                     //range.selectNode(sel);
24224                     
24225                     
24226                     var selection = editorcore.getSelection();
24227                     selection.removeAllRanges();
24228                     selection.addRange(range);
24229                     
24230                     
24231                     
24232                     //_this.updateToolbar(null, null, pn);
24233                     _this.updateToolbar(null, null, null);
24234                     _this.footDisp.dom.innerHTML = ''; 
24235                 }
24236             }
24237             
24238                     
24239                 
24240             
24241         });
24242         
24243         
24244         tb.el.on('click', function(e){
24245             e.preventDefault(); // what does this do?
24246         });
24247         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24248         tb.el.hide();
24249         tb.name = nm;
24250         // dont need to disable them... as they will get hidden
24251         return tb;
24252          
24253         
24254     },
24255     buildFooter : function()
24256     {
24257         
24258         var fel = this.editor.wrap.createChild();
24259         this.footer = new Roo.Toolbar(fel);
24260         // toolbar has scrolly on left / right?
24261         var footDisp= new Roo.Toolbar.Fill();
24262         var _t = this;
24263         this.footer.add(
24264             {
24265                 text : '&lt;',
24266                 xtype: 'Button',
24267                 handler : function() {
24268                     _t.footDisp.scrollTo('left',0,true)
24269                 }
24270             }
24271         );
24272         this.footer.add( footDisp );
24273         this.footer.add( 
24274             {
24275                 text : '&gt;',
24276                 xtype: 'Button',
24277                 handler : function() {
24278                     // no animation..
24279                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24280                 }
24281             }
24282         );
24283         var fel = Roo.get(footDisp.el);
24284         fel.addClass('x-editor-context');
24285         this.footDispWrap = fel; 
24286         this.footDispWrap.overflow  = 'hidden';
24287         
24288         this.footDisp = fel.createChild();
24289         this.footDispWrap.on('click', this.onContextClick, this)
24290         
24291         
24292     },
24293     onContextClick : function (ev,dom)
24294     {
24295         ev.preventDefault();
24296         var  cn = dom.className;
24297         //Roo.log(cn);
24298         if (!cn.match(/x-ed-loc-/)) {
24299             return;
24300         }
24301         var n = cn.split('-').pop();
24302         var ans = this.footerEls;
24303         var sel = ans[n];
24304         
24305          // pick
24306         var range = this.editorcore.createRange();
24307         
24308         range.selectNodeContents(sel);
24309         //range.selectNode(sel);
24310         
24311         
24312         var selection = this.editorcore.getSelection();
24313         selection.removeAllRanges();
24314         selection.addRange(range);
24315         
24316         
24317         
24318         this.updateToolbar(null, null, sel);
24319         
24320         
24321     }
24322     
24323     
24324     
24325     
24326     
24327 });
24328
24329
24330
24331
24332
24333 /*
24334  * Based on:
24335  * Ext JS Library 1.1.1
24336  * Copyright(c) 2006-2007, Ext JS, LLC.
24337  *
24338  * Originally Released Under LGPL - original licence link has changed is not relivant.
24339  *
24340  * Fork - LGPL
24341  * <script type="text/javascript">
24342  */
24343  
24344 /**
24345  * @class Roo.form.BasicForm
24346  * @extends Roo.util.Observable
24347  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24348  * @constructor
24349  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24350  * @param {Object} config Configuration options
24351  */
24352 Roo.form.BasicForm = function(el, config){
24353     this.allItems = [];
24354     this.childForms = [];
24355     Roo.apply(this, config);
24356     /*
24357      * The Roo.form.Field items in this form.
24358      * @type MixedCollection
24359      */
24360      
24361      
24362     this.items = new Roo.util.MixedCollection(false, function(o){
24363         return o.id || (o.id = Roo.id());
24364     });
24365     this.addEvents({
24366         /**
24367          * @event beforeaction
24368          * Fires before any action is performed. Return false to cancel the action.
24369          * @param {Form} this
24370          * @param {Action} action The action to be performed
24371          */
24372         beforeaction: true,
24373         /**
24374          * @event actionfailed
24375          * Fires when an action fails.
24376          * @param {Form} this
24377          * @param {Action} action The action that failed
24378          */
24379         actionfailed : true,
24380         /**
24381          * @event actioncomplete
24382          * Fires when an action is completed.
24383          * @param {Form} this
24384          * @param {Action} action The action that completed
24385          */
24386         actioncomplete : true
24387     });
24388     if(el){
24389         this.initEl(el);
24390     }
24391     Roo.form.BasicForm.superclass.constructor.call(this);
24392     
24393     Roo.form.BasicForm.popover.apply();
24394 };
24395
24396 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24397     /**
24398      * @cfg {String} method
24399      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24400      */
24401     /**
24402      * @cfg {DataReader} reader
24403      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24404      * This is optional as there is built-in support for processing JSON.
24405      */
24406     /**
24407      * @cfg {DataReader} errorReader
24408      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24409      * This is completely optional as there is built-in support for processing JSON.
24410      */
24411     /**
24412      * @cfg {String} url
24413      * The URL to use for form actions if one isn't supplied in the action options.
24414      */
24415     /**
24416      * @cfg {Boolean} fileUpload
24417      * Set to true if this form is a file upload.
24418      */
24419      
24420     /**
24421      * @cfg {Object} baseParams
24422      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24423      */
24424      /**
24425      
24426     /**
24427      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24428      */
24429     timeout: 30,
24430
24431     // private
24432     activeAction : null,
24433
24434     /**
24435      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24436      * or setValues() data instead of when the form was first created.
24437      */
24438     trackResetOnLoad : false,
24439     
24440     
24441     /**
24442      * childForms - used for multi-tab forms
24443      * @type {Array}
24444      */
24445     childForms : false,
24446     
24447     /**
24448      * allItems - full list of fields.
24449      * @type {Array}
24450      */
24451     allItems : false,
24452     
24453     /**
24454      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24455      * element by passing it or its id or mask the form itself by passing in true.
24456      * @type Mixed
24457      */
24458     waitMsgTarget : false,
24459     
24460     /**
24461      * @type Boolean
24462      */
24463     disableMask : false,
24464     
24465     /**
24466      * @cfg {Boolean} errorMask (true|false) default false
24467      */
24468     errorMask : false,
24469     
24470     /**
24471      * @cfg {Number} maskOffset Default 100
24472      */
24473     maskOffset : 100,
24474
24475     // private
24476     initEl : function(el){
24477         this.el = Roo.get(el);
24478         this.id = this.el.id || Roo.id();
24479         this.el.on('submit', this.onSubmit, this);
24480         this.el.addClass('x-form');
24481     },
24482
24483     // private
24484     onSubmit : function(e){
24485         e.stopEvent();
24486     },
24487
24488     /**
24489      * Returns true if client-side validation on the form is successful.
24490      * @return Boolean
24491      */
24492     isValid : function(){
24493         var valid = true;
24494         var target = false;
24495         this.items.each(function(f){
24496             if(f.validate()){
24497                 valid = false;
24498                 
24499                 if(!target && f.el.isVisible(true)){
24500                     target = f;
24501                 }
24502            }
24503         });
24504         
24505         if(this.errorMask && !valid){
24506             Roo.form.BasicForm.popover.mask(this, target);
24507         }
24508         
24509         return valid;
24510     },
24511
24512     /**
24513      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24514      * @return Boolean
24515      */
24516     isDirty : function(){
24517         var dirty = false;
24518         this.items.each(function(f){
24519            if(f.isDirty()){
24520                dirty = true;
24521                return false;
24522            }
24523         });
24524         return dirty;
24525     },
24526     
24527     /**
24528      * Returns true if any fields in this form have changed since their original load. (New version)
24529      * @return Boolean
24530      */
24531     
24532     hasChanged : function()
24533     {
24534         var dirty = false;
24535         this.items.each(function(f){
24536            if(f.hasChanged()){
24537                dirty = true;
24538                return false;
24539            }
24540         });
24541         return dirty;
24542         
24543     },
24544     /**
24545      * Resets all hasChanged to 'false' -
24546      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24547      * So hasChanged storage is only to be used for this purpose
24548      * @return Boolean
24549      */
24550     resetHasChanged : function()
24551     {
24552         this.items.each(function(f){
24553            f.resetHasChanged();
24554         });
24555         
24556     },
24557     
24558     
24559     /**
24560      * Performs a predefined action (submit or load) or custom actions you define on this form.
24561      * @param {String} actionName The name of the action type
24562      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24563      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24564      * accept other config options):
24565      * <pre>
24566 Property          Type             Description
24567 ----------------  ---------------  ----------------------------------------------------------------------------------
24568 url               String           The url for the action (defaults to the form's url)
24569 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24570 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24571 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24572                                    validate the form on the client (defaults to false)
24573      * </pre>
24574      * @return {BasicForm} this
24575      */
24576     doAction : function(action, options){
24577         if(typeof action == 'string'){
24578             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24579         }
24580         if(this.fireEvent('beforeaction', this, action) !== false){
24581             this.beforeAction(action);
24582             action.run.defer(100, action);
24583         }
24584         return this;
24585     },
24586
24587     /**
24588      * Shortcut to do a submit action.
24589      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24590      * @return {BasicForm} this
24591      */
24592     submit : function(options){
24593         this.doAction('submit', options);
24594         return this;
24595     },
24596
24597     /**
24598      * Shortcut to do a load action.
24599      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24600      * @return {BasicForm} this
24601      */
24602     load : function(options){
24603         this.doAction('load', options);
24604         return this;
24605     },
24606
24607     /**
24608      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24609      * @param {Record} record The record to edit
24610      * @return {BasicForm} this
24611      */
24612     updateRecord : function(record){
24613         record.beginEdit();
24614         var fs = record.fields;
24615         fs.each(function(f){
24616             var field = this.findField(f.name);
24617             if(field){
24618                 record.set(f.name, field.getValue());
24619             }
24620         }, this);
24621         record.endEdit();
24622         return this;
24623     },
24624
24625     /**
24626      * Loads an Roo.data.Record into this form.
24627      * @param {Record} record The record to load
24628      * @return {BasicForm} this
24629      */
24630     loadRecord : function(record){
24631         this.setValues(record.data);
24632         return this;
24633     },
24634
24635     // private
24636     beforeAction : function(action){
24637         var o = action.options;
24638         
24639         if(!this.disableMask) {
24640             if(this.waitMsgTarget === true){
24641                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24642             }else if(this.waitMsgTarget){
24643                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24644                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24645             }else {
24646                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24647             }
24648         }
24649         
24650          
24651     },
24652
24653     // private
24654     afterAction : function(action, success){
24655         this.activeAction = null;
24656         var o = action.options;
24657         
24658         if(!this.disableMask) {
24659             if(this.waitMsgTarget === true){
24660                 this.el.unmask();
24661             }else if(this.waitMsgTarget){
24662                 this.waitMsgTarget.unmask();
24663             }else{
24664                 Roo.MessageBox.updateProgress(1);
24665                 Roo.MessageBox.hide();
24666             }
24667         }
24668         
24669         if(success){
24670             if(o.reset){
24671                 this.reset();
24672             }
24673             Roo.callback(o.success, o.scope, [this, action]);
24674             this.fireEvent('actioncomplete', this, action);
24675             
24676         }else{
24677             
24678             // failure condition..
24679             // we have a scenario where updates need confirming.
24680             // eg. if a locking scenario exists..
24681             // we look for { errors : { needs_confirm : true }} in the response.
24682             if (
24683                 (typeof(action.result) != 'undefined')  &&
24684                 (typeof(action.result.errors) != 'undefined')  &&
24685                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24686            ){
24687                 var _t = this;
24688                 Roo.MessageBox.confirm(
24689                     "Change requires confirmation",
24690                     action.result.errorMsg,
24691                     function(r) {
24692                         if (r != 'yes') {
24693                             return;
24694                         }
24695                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24696                     }
24697                     
24698                 );
24699                 
24700                 
24701                 
24702                 return;
24703             }
24704             
24705             Roo.callback(o.failure, o.scope, [this, action]);
24706             // show an error message if no failed handler is set..
24707             if (!this.hasListener('actionfailed')) {
24708                 Roo.MessageBox.alert("Error",
24709                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24710                         action.result.errorMsg :
24711                         "Saving Failed, please check your entries or try again"
24712                 );
24713             }
24714             
24715             this.fireEvent('actionfailed', this, action);
24716         }
24717         
24718     },
24719
24720     /**
24721      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24722      * @param {String} id The value to search for
24723      * @return Field
24724      */
24725     findField : function(id){
24726         var field = this.items.get(id);
24727         if(!field){
24728             this.items.each(function(f){
24729                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24730                     field = f;
24731                     return false;
24732                 }
24733             });
24734         }
24735         return field || null;
24736     },
24737
24738     /**
24739      * Add a secondary form to this one, 
24740      * Used to provide tabbed forms. One form is primary, with hidden values 
24741      * which mirror the elements from the other forms.
24742      * 
24743      * @param {Roo.form.Form} form to add.
24744      * 
24745      */
24746     addForm : function(form)
24747     {
24748        
24749         if (this.childForms.indexOf(form) > -1) {
24750             // already added..
24751             return;
24752         }
24753         this.childForms.push(form);
24754         var n = '';
24755         Roo.each(form.allItems, function (fe) {
24756             
24757             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24758             if (this.findField(n)) { // already added..
24759                 return;
24760             }
24761             var add = new Roo.form.Hidden({
24762                 name : n
24763             });
24764             add.render(this.el);
24765             
24766             this.add( add );
24767         }, this);
24768         
24769     },
24770     /**
24771      * Mark fields in this form invalid in bulk.
24772      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24773      * @return {BasicForm} this
24774      */
24775     markInvalid : function(errors){
24776         if(errors instanceof Array){
24777             for(var i = 0, len = errors.length; i < len; i++){
24778                 var fieldError = errors[i];
24779                 var f = this.findField(fieldError.id);
24780                 if(f){
24781                     f.markInvalid(fieldError.msg);
24782                 }
24783             }
24784         }else{
24785             var field, id;
24786             for(id in errors){
24787                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24788                     field.markInvalid(errors[id]);
24789                 }
24790             }
24791         }
24792         Roo.each(this.childForms || [], function (f) {
24793             f.markInvalid(errors);
24794         });
24795         
24796         return this;
24797     },
24798
24799     /**
24800      * Set values for fields in this form in bulk.
24801      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24802      * @return {BasicForm} this
24803      */
24804     setValues : function(values){
24805         if(values instanceof Array){ // array of objects
24806             for(var i = 0, len = values.length; i < len; i++){
24807                 var v = values[i];
24808                 var f = this.findField(v.id);
24809                 if(f){
24810                     f.setValue(v.value);
24811                     if(this.trackResetOnLoad){
24812                         f.originalValue = f.getValue();
24813                     }
24814                 }
24815             }
24816         }else{ // object hash
24817             var field, id;
24818             for(id in values){
24819                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24820                     
24821                     if (field.setFromData && 
24822                         field.valueField && 
24823                         field.displayField &&
24824                         // combos' with local stores can 
24825                         // be queried via setValue()
24826                         // to set their value..
24827                         (field.store && !field.store.isLocal)
24828                         ) {
24829                         // it's a combo
24830                         var sd = { };
24831                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24832                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24833                         field.setFromData(sd);
24834                         
24835                     } else {
24836                         field.setValue(values[id]);
24837                     }
24838                     
24839                     
24840                     if(this.trackResetOnLoad){
24841                         field.originalValue = field.getValue();
24842                     }
24843                 }
24844             }
24845         }
24846         this.resetHasChanged();
24847         
24848         
24849         Roo.each(this.childForms || [], function (f) {
24850             f.setValues(values);
24851             f.resetHasChanged();
24852         });
24853                 
24854         return this;
24855     },
24856
24857     /**
24858      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24859      * they are returned as an array.
24860      * @param {Boolean} asString
24861      * @return {Object}
24862      */
24863     getValues : function(asString){
24864         if (this.childForms) {
24865             // copy values from the child forms
24866             Roo.each(this.childForms, function (f) {
24867                 this.setValues(f.getValues());
24868             }, this);
24869         }
24870         
24871         
24872         
24873         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
24874         if(asString === true){
24875             return fs;
24876         }
24877         return Roo.urlDecode(fs);
24878     },
24879     
24880     /**
24881      * Returns the fields in this form as an object with key/value pairs. 
24882      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
24883      * @return {Object}
24884      */
24885     getFieldValues : function(with_hidden)
24886     {
24887         if (this.childForms) {
24888             // copy values from the child forms
24889             // should this call getFieldValues - probably not as we do not currently copy
24890             // hidden fields when we generate..
24891             Roo.each(this.childForms, function (f) {
24892                 this.setValues(f.getValues());
24893             }, this);
24894         }
24895         
24896         var ret = {};
24897         this.items.each(function(f){
24898             if (!f.getName()) {
24899                 return;
24900             }
24901             var v = f.getValue();
24902             if (f.inputType =='radio') {
24903                 if (typeof(ret[f.getName()]) == 'undefined') {
24904                     ret[f.getName()] = ''; // empty..
24905                 }
24906                 
24907                 if (!f.el.dom.checked) {
24908                     return;
24909                     
24910                 }
24911                 v = f.el.dom.value;
24912                 
24913             }
24914             
24915             // not sure if this supported any more..
24916             if ((typeof(v) == 'object') && f.getRawValue) {
24917                 v = f.getRawValue() ; // dates..
24918             }
24919             // combo boxes where name != hiddenName...
24920             if (f.name != f.getName()) {
24921                 ret[f.name] = f.getRawValue();
24922             }
24923             ret[f.getName()] = v;
24924         });
24925         
24926         return ret;
24927     },
24928
24929     /**
24930      * Clears all invalid messages in this form.
24931      * @return {BasicForm} this
24932      */
24933     clearInvalid : function(){
24934         this.items.each(function(f){
24935            f.clearInvalid();
24936         });
24937         
24938         Roo.each(this.childForms || [], function (f) {
24939             f.clearInvalid();
24940         });
24941         
24942         
24943         return this;
24944     },
24945
24946     /**
24947      * Resets this form.
24948      * @return {BasicForm} this
24949      */
24950     reset : function(){
24951         this.items.each(function(f){
24952             f.reset();
24953         });
24954         
24955         Roo.each(this.childForms || [], function (f) {
24956             f.reset();
24957         });
24958         this.resetHasChanged();
24959         
24960         return this;
24961     },
24962
24963     /**
24964      * Add Roo.form components to this form.
24965      * @param {Field} field1
24966      * @param {Field} field2 (optional)
24967      * @param {Field} etc (optional)
24968      * @return {BasicForm} this
24969      */
24970     add : function(){
24971         this.items.addAll(Array.prototype.slice.call(arguments, 0));
24972         return this;
24973     },
24974
24975
24976     /**
24977      * Removes a field from the items collection (does NOT remove its markup).
24978      * @param {Field} field
24979      * @return {BasicForm} this
24980      */
24981     remove : function(field){
24982         this.items.remove(field);
24983         return this;
24984     },
24985
24986     /**
24987      * Looks at the fields in this form, checks them for an id attribute,
24988      * and calls applyTo on the existing dom element with that id.
24989      * @return {BasicForm} this
24990      */
24991     render : function(){
24992         this.items.each(function(f){
24993             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
24994                 f.applyTo(f.id);
24995             }
24996         });
24997         return this;
24998     },
24999
25000     /**
25001      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25002      * @param {Object} values
25003      * @return {BasicForm} this
25004      */
25005     applyToFields : function(o){
25006         this.items.each(function(f){
25007            Roo.apply(f, o);
25008         });
25009         return this;
25010     },
25011
25012     /**
25013      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25014      * @param {Object} values
25015      * @return {BasicForm} this
25016      */
25017     applyIfToFields : function(o){
25018         this.items.each(function(f){
25019            Roo.applyIf(f, o);
25020         });
25021         return this;
25022     }
25023 });
25024
25025 // back compat
25026 Roo.BasicForm = Roo.form.BasicForm;
25027
25028 Roo.apply(Roo.form.BasicForm, {
25029     
25030     popover : {
25031         
25032         padding : 5,
25033         
25034         isApplied : false,
25035         
25036         isMasked : false,
25037         
25038         form : false,
25039         
25040         target : false,
25041         
25042         toolTip : false,
25043         
25044         intervalID : false,
25045         
25046         maskEl : false,
25047         
25048         apply : function()
25049         {
25050             if(this.isApplied){
25051                 return;
25052             }
25053             
25054             this.maskEl = {
25055                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25056                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25057                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25058                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25059             };
25060             
25061             this.maskEl.top.enableDisplayMode("block");
25062             this.maskEl.left.enableDisplayMode("block");
25063             this.maskEl.bottom.enableDisplayMode("block");
25064             this.maskEl.right.enableDisplayMode("block");
25065             
25066 //            this.toolTip = new Roo.bootstrap.Tooltip({
25067 //                cls : 'roo-form-error-popover',
25068 //                alignment : {
25069 //                    'left' : ['r-l', [-2,0], 'right'],
25070 //                    'right' : ['l-r', [2,0], 'left'],
25071 //                    'bottom' : ['tl-bl', [0,2], 'top'],
25072 //                    'top' : [ 'bl-tl', [0,-2], 'bottom']
25073 //                }
25074 //            });
25075 //            
25076 //            this.toolTip.render(Roo.get(document.body));
25077 //
25078 //            this.toolTip.el.enableDisplayMode("block");
25079             
25080             Roo.get(document.body).on('click', function(){
25081                 this.unmask();
25082             }, this);
25083             
25084             Roo.get(document.body).on('touchstart', function(){
25085                 this.unmask();
25086             }, this);
25087             
25088             this.isApplied = true
25089         },
25090         
25091         mask : function(form, target)
25092         {
25093             this.form = form;
25094             
25095             this.target = target;
25096             
25097             if(!this.form.errorMask || !target.el){
25098                 return;
25099             }
25100             
25101             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25102             
25103             Roo.log(scrollable);
25104             
25105             var ot = this.target.el.calcOffsetsTo(scrollable);
25106             
25107             var scrollTo = ot[1] - this.form.maskOffset;
25108             
25109             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25110             
25111             scrollable.scrollTo('top', scrollTo);
25112             
25113             var box = this.target.el.getBox();
25114             Roo.log(box);
25115             var zIndex = Roo.bootstrap.Modal.zIndex++;
25116
25117             
25118             this.maskEl.top.setStyle('position', 'absolute');
25119             this.maskEl.top.setStyle('z-index', zIndex);
25120             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25121             this.maskEl.top.setLeft(0);
25122             this.maskEl.top.setTop(0);
25123             this.maskEl.top.show();
25124             
25125             this.maskEl.left.setStyle('position', 'absolute');
25126             this.maskEl.left.setStyle('z-index', zIndex);
25127             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25128             this.maskEl.left.setLeft(0);
25129             this.maskEl.left.setTop(box.y - this.padding);
25130             this.maskEl.left.show();
25131
25132             this.maskEl.bottom.setStyle('position', 'absolute');
25133             this.maskEl.bottom.setStyle('z-index', zIndex);
25134             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25135             this.maskEl.bottom.setLeft(0);
25136             this.maskEl.bottom.setTop(box.bottom + this.padding);
25137             this.maskEl.bottom.show();
25138
25139             this.maskEl.right.setStyle('position', 'absolute');
25140             this.maskEl.right.setStyle('z-index', zIndex);
25141             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25142             this.maskEl.right.setLeft(box.right + this.padding);
25143             this.maskEl.right.setTop(box.y - this.padding);
25144             this.maskEl.right.show();
25145
25146             this.toolTip.bindEl = this.target.el;
25147
25148             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
25149
25150             var tip = this.target.blankText;
25151
25152             if(this.target.getValue() !== '' ) {
25153                 
25154                 if (this.target.invalidText.length) {
25155                     tip = this.target.invalidText;
25156                 } else if (this.target.regexText.length){
25157                     tip = this.target.regexText;
25158                 }
25159             }
25160
25161             this.toolTip.show(tip);
25162
25163             this.intervalID = window.setInterval(function() {
25164                 Roo.bootstrap.Form.popover.unmask();
25165             }, 10000);
25166
25167             window.onwheel = function(){ return false;};
25168             
25169             (function(){ this.isMasked = true; }).defer(500, this);
25170             
25171         },
25172         
25173         unmask : function()
25174         {
25175             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25176                 return;
25177             }
25178             
25179             this.maskEl.top.setStyle('position', 'absolute');
25180             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25181             this.maskEl.top.hide();
25182
25183             this.maskEl.left.setStyle('position', 'absolute');
25184             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25185             this.maskEl.left.hide();
25186
25187             this.maskEl.bottom.setStyle('position', 'absolute');
25188             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25189             this.maskEl.bottom.hide();
25190
25191             this.maskEl.right.setStyle('position', 'absolute');
25192             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25193             this.maskEl.right.hide();
25194             
25195 //            this.toolTip.hide();
25196             
25197 //            this.toolTip.el.hide();
25198             
25199             window.onwheel = function(){ return true;};
25200             
25201             if(this.intervalID){
25202                 window.clearInterval(this.intervalID);
25203                 this.intervalID = false;
25204             }
25205             
25206             this.isMasked = false;
25207             
25208         }
25209         
25210     }
25211     
25212 });/*
25213  * Based on:
25214  * Ext JS Library 1.1.1
25215  * Copyright(c) 2006-2007, Ext JS, LLC.
25216  *
25217  * Originally Released Under LGPL - original licence link has changed is not relivant.
25218  *
25219  * Fork - LGPL
25220  * <script type="text/javascript">
25221  */
25222
25223 /**
25224  * @class Roo.form.Form
25225  * @extends Roo.form.BasicForm
25226  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25227  * @constructor
25228  * @param {Object} config Configuration options
25229  */
25230 Roo.form.Form = function(config){
25231     var xitems =  [];
25232     if (config.items) {
25233         xitems = config.items;
25234         delete config.items;
25235     }
25236    
25237     
25238     Roo.form.Form.superclass.constructor.call(this, null, config);
25239     this.url = this.url || this.action;
25240     if(!this.root){
25241         this.root = new Roo.form.Layout(Roo.applyIf({
25242             id: Roo.id()
25243         }, config));
25244     }
25245     this.active = this.root;
25246     /**
25247      * Array of all the buttons that have been added to this form via {@link addButton}
25248      * @type Array
25249      */
25250     this.buttons = [];
25251     this.allItems = [];
25252     this.addEvents({
25253         /**
25254          * @event clientvalidation
25255          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25256          * @param {Form} this
25257          * @param {Boolean} valid true if the form has passed client-side validation
25258          */
25259         clientvalidation: true,
25260         /**
25261          * @event rendered
25262          * Fires when the form is rendered
25263          * @param {Roo.form.Form} form
25264          */
25265         rendered : true
25266     });
25267     
25268     if (this.progressUrl) {
25269             // push a hidden field onto the list of fields..
25270             this.addxtype( {
25271                     xns: Roo.form, 
25272                     xtype : 'Hidden', 
25273                     name : 'UPLOAD_IDENTIFIER' 
25274             });
25275         }
25276         
25277     
25278     Roo.each(xitems, this.addxtype, this);
25279     
25280 };
25281
25282 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25283     /**
25284      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25285      */
25286     /**
25287      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25288      */
25289     /**
25290      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25291      */
25292     buttonAlign:'center',
25293
25294     /**
25295      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25296      */
25297     minButtonWidth:75,
25298
25299     /**
25300      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25301      * This property cascades to child containers if not set.
25302      */
25303     labelAlign:'left',
25304
25305     /**
25306      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25307      * fires a looping event with that state. This is required to bind buttons to the valid
25308      * state using the config value formBind:true on the button.
25309      */
25310     monitorValid : false,
25311
25312     /**
25313      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25314      */
25315     monitorPoll : 200,
25316     
25317     /**
25318      * @cfg {String} progressUrl - Url to return progress data 
25319      */
25320     
25321     progressUrl : false,
25322     /**
25323      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25324      * sending a formdata with extra parameters - eg uploaded elements.
25325      */
25326     
25327     formData : false,
25328     
25329     /**
25330      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25331      * fields are added and the column is closed. If no fields are passed the column remains open
25332      * until end() is called.
25333      * @param {Object} config The config to pass to the column
25334      * @param {Field} field1 (optional)
25335      * @param {Field} field2 (optional)
25336      * @param {Field} etc (optional)
25337      * @return Column The column container object
25338      */
25339     column : function(c){
25340         var col = new Roo.form.Column(c);
25341         this.start(col);
25342         if(arguments.length > 1){ // duplicate code required because of Opera
25343             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25344             this.end();
25345         }
25346         return col;
25347     },
25348
25349     /**
25350      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25351      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25352      * until end() is called.
25353      * @param {Object} config The config to pass to the fieldset
25354      * @param {Field} field1 (optional)
25355      * @param {Field} field2 (optional)
25356      * @param {Field} etc (optional)
25357      * @return FieldSet The fieldset container object
25358      */
25359     fieldset : function(c){
25360         var fs = new Roo.form.FieldSet(c);
25361         this.start(fs);
25362         if(arguments.length > 1){ // duplicate code required because of Opera
25363             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25364             this.end();
25365         }
25366         return fs;
25367     },
25368
25369     /**
25370      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25371      * fields are added and the container is closed. If no fields are passed the container remains open
25372      * until end() is called.
25373      * @param {Object} config The config to pass to the Layout
25374      * @param {Field} field1 (optional)
25375      * @param {Field} field2 (optional)
25376      * @param {Field} etc (optional)
25377      * @return Layout The container object
25378      */
25379     container : function(c){
25380         var l = new Roo.form.Layout(c);
25381         this.start(l);
25382         if(arguments.length > 1){ // duplicate code required because of Opera
25383             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25384             this.end();
25385         }
25386         return l;
25387     },
25388
25389     /**
25390      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25391      * @param {Object} container A Roo.form.Layout or subclass of Layout
25392      * @return {Form} this
25393      */
25394     start : function(c){
25395         // cascade label info
25396         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25397         this.active.stack.push(c);
25398         c.ownerCt = this.active;
25399         this.active = c;
25400         return this;
25401     },
25402
25403     /**
25404      * Closes the current open container
25405      * @return {Form} this
25406      */
25407     end : function(){
25408         if(this.active == this.root){
25409             return this;
25410         }
25411         this.active = this.active.ownerCt;
25412         return this;
25413     },
25414
25415     /**
25416      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25417      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25418      * as the label of the field.
25419      * @param {Field} field1
25420      * @param {Field} field2 (optional)
25421      * @param {Field} etc. (optional)
25422      * @return {Form} this
25423      */
25424     add : function(){
25425         this.active.stack.push.apply(this.active.stack, arguments);
25426         this.allItems.push.apply(this.allItems,arguments);
25427         var r = [];
25428         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25429             if(a[i].isFormField){
25430                 r.push(a[i]);
25431             }
25432         }
25433         if(r.length > 0){
25434             Roo.form.Form.superclass.add.apply(this, r);
25435         }
25436         return this;
25437     },
25438     
25439
25440     
25441     
25442     
25443      /**
25444      * Find any element that has been added to a form, using it's ID or name
25445      * This can include framesets, columns etc. along with regular fields..
25446      * @param {String} id - id or name to find.
25447      
25448      * @return {Element} e - or false if nothing found.
25449      */
25450     findbyId : function(id)
25451     {
25452         var ret = false;
25453         if (!id) {
25454             return ret;
25455         }
25456         Roo.each(this.allItems, function(f){
25457             if (f.id == id || f.name == id ){
25458                 ret = f;
25459                 return false;
25460             }
25461         });
25462         return ret;
25463     },
25464
25465     
25466     
25467     /**
25468      * Render this form into the passed container. This should only be called once!
25469      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25470      * @return {Form} this
25471      */
25472     render : function(ct)
25473     {
25474         
25475         
25476         
25477         ct = Roo.get(ct);
25478         var o = this.autoCreate || {
25479             tag: 'form',
25480             method : this.method || 'POST',
25481             id : this.id || Roo.id()
25482         };
25483         this.initEl(ct.createChild(o));
25484
25485         this.root.render(this.el);
25486         
25487        
25488              
25489         this.items.each(function(f){
25490             f.render('x-form-el-'+f.id);
25491         });
25492
25493         if(this.buttons.length > 0){
25494             // tables are required to maintain order and for correct IE layout
25495             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25496                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25497                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25498             }}, null, true);
25499             var tr = tb.getElementsByTagName('tr')[0];
25500             for(var i = 0, len = this.buttons.length; i < len; i++) {
25501                 var b = this.buttons[i];
25502                 var td = document.createElement('td');
25503                 td.className = 'x-form-btn-td';
25504                 b.render(tr.appendChild(td));
25505             }
25506         }
25507         if(this.monitorValid){ // initialize after render
25508             this.startMonitoring();
25509         }
25510         this.fireEvent('rendered', this);
25511         return this;
25512     },
25513
25514     /**
25515      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25516      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25517      * object or a valid Roo.DomHelper element config
25518      * @param {Function} handler The function called when the button is clicked
25519      * @param {Object} scope (optional) The scope of the handler function
25520      * @return {Roo.Button}
25521      */
25522     addButton : function(config, handler, scope){
25523         var bc = {
25524             handler: handler,
25525             scope: scope,
25526             minWidth: this.minButtonWidth,
25527             hideParent:true
25528         };
25529         if(typeof config == "string"){
25530             bc.text = config;
25531         }else{
25532             Roo.apply(bc, config);
25533         }
25534         var btn = new Roo.Button(null, bc);
25535         this.buttons.push(btn);
25536         return btn;
25537     },
25538
25539      /**
25540      * Adds a series of form elements (using the xtype property as the factory method.
25541      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25542      * @param {Object} config 
25543      */
25544     
25545     addxtype : function()
25546     {
25547         var ar = Array.prototype.slice.call(arguments, 0);
25548         var ret = false;
25549         for(var i = 0; i < ar.length; i++) {
25550             if (!ar[i]) {
25551                 continue; // skip -- if this happends something invalid got sent, we 
25552                 // should ignore it, as basically that interface element will not show up
25553                 // and that should be pretty obvious!!
25554             }
25555             
25556             if (Roo.form[ar[i].xtype]) {
25557                 ar[i].form = this;
25558                 var fe = Roo.factory(ar[i], Roo.form);
25559                 if (!ret) {
25560                     ret = fe;
25561                 }
25562                 fe.form = this;
25563                 if (fe.store) {
25564                     fe.store.form = this;
25565                 }
25566                 if (fe.isLayout) {  
25567                          
25568                     this.start(fe);
25569                     this.allItems.push(fe);
25570                     if (fe.items && fe.addxtype) {
25571                         fe.addxtype.apply(fe, fe.items);
25572                         delete fe.items;
25573                     }
25574                      this.end();
25575                     continue;
25576                 }
25577                 
25578                 
25579                  
25580                 this.add(fe);
25581               //  console.log('adding ' + ar[i].xtype);
25582             }
25583             if (ar[i].xtype == 'Button') {  
25584                 //console.log('adding button');
25585                 //console.log(ar[i]);
25586                 this.addButton(ar[i]);
25587                 this.allItems.push(fe);
25588                 continue;
25589             }
25590             
25591             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25592                 alert('end is not supported on xtype any more, use items');
25593             //    this.end();
25594             //    //console.log('adding end');
25595             }
25596             
25597         }
25598         return ret;
25599     },
25600     
25601     /**
25602      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25603      * option "monitorValid"
25604      */
25605     startMonitoring : function(){
25606         if(!this.bound){
25607             this.bound = true;
25608             Roo.TaskMgr.start({
25609                 run : this.bindHandler,
25610                 interval : this.monitorPoll || 200,
25611                 scope: this
25612             });
25613         }
25614     },
25615
25616     /**
25617      * Stops monitoring of the valid state of this form
25618      */
25619     stopMonitoring : function(){
25620         this.bound = false;
25621     },
25622
25623     // private
25624     bindHandler : function(){
25625         if(!this.bound){
25626             return false; // stops binding
25627         }
25628         var valid = true;
25629         this.items.each(function(f){
25630             if(!f.isValid(true)){
25631                 valid = false;
25632                 return false;
25633             }
25634         });
25635         for(var i = 0, len = this.buttons.length; i < len; i++){
25636             var btn = this.buttons[i];
25637             if(btn.formBind === true && btn.disabled === valid){
25638                 btn.setDisabled(!valid);
25639             }
25640         }
25641         this.fireEvent('clientvalidation', this, valid);
25642     }
25643     
25644     
25645     
25646     
25647     
25648     
25649     
25650     
25651 });
25652
25653
25654 // back compat
25655 Roo.Form = Roo.form.Form;
25656 /*
25657  * Based on:
25658  * Ext JS Library 1.1.1
25659  * Copyright(c) 2006-2007, Ext JS, LLC.
25660  *
25661  * Originally Released Under LGPL - original licence link has changed is not relivant.
25662  *
25663  * Fork - LGPL
25664  * <script type="text/javascript">
25665  */
25666
25667 // as we use this in bootstrap.
25668 Roo.namespace('Roo.form');
25669  /**
25670  * @class Roo.form.Action
25671  * Internal Class used to handle form actions
25672  * @constructor
25673  * @param {Roo.form.BasicForm} el The form element or its id
25674  * @param {Object} config Configuration options
25675  */
25676
25677  
25678  
25679 // define the action interface
25680 Roo.form.Action = function(form, options){
25681     this.form = form;
25682     this.options = options || {};
25683 };
25684 /**
25685  * Client Validation Failed
25686  * @const 
25687  */
25688 Roo.form.Action.CLIENT_INVALID = 'client';
25689 /**
25690  * Server Validation Failed
25691  * @const 
25692  */
25693 Roo.form.Action.SERVER_INVALID = 'server';
25694  /**
25695  * Connect to Server Failed
25696  * @const 
25697  */
25698 Roo.form.Action.CONNECT_FAILURE = 'connect';
25699 /**
25700  * Reading Data from Server Failed
25701  * @const 
25702  */
25703 Roo.form.Action.LOAD_FAILURE = 'load';
25704
25705 Roo.form.Action.prototype = {
25706     type : 'default',
25707     failureType : undefined,
25708     response : undefined,
25709     result : undefined,
25710
25711     // interface method
25712     run : function(options){
25713
25714     },
25715
25716     // interface method
25717     success : function(response){
25718
25719     },
25720
25721     // interface method
25722     handleResponse : function(response){
25723
25724     },
25725
25726     // default connection failure
25727     failure : function(response){
25728         
25729         this.response = response;
25730         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25731         this.form.afterAction(this, false);
25732     },
25733
25734     processResponse : function(response){
25735         this.response = response;
25736         if(!response.responseText){
25737             return true;
25738         }
25739         this.result = this.handleResponse(response);
25740         return this.result;
25741     },
25742
25743     // utility functions used internally
25744     getUrl : function(appendParams){
25745         var url = this.options.url || this.form.url || this.form.el.dom.action;
25746         if(appendParams){
25747             var p = this.getParams();
25748             if(p){
25749                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25750             }
25751         }
25752         return url;
25753     },
25754
25755     getMethod : function(){
25756         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25757     },
25758
25759     getParams : function(){
25760         var bp = this.form.baseParams;
25761         var p = this.options.params;
25762         if(p){
25763             if(typeof p == "object"){
25764                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25765             }else if(typeof p == 'string' && bp){
25766                 p += '&' + Roo.urlEncode(bp);
25767             }
25768         }else if(bp){
25769             p = Roo.urlEncode(bp);
25770         }
25771         return p;
25772     },
25773
25774     createCallback : function(){
25775         return {
25776             success: this.success,
25777             failure: this.failure,
25778             scope: this,
25779             timeout: (this.form.timeout*1000),
25780             upload: this.form.fileUpload ? this.success : undefined
25781         };
25782     }
25783 };
25784
25785 Roo.form.Action.Submit = function(form, options){
25786     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25787 };
25788
25789 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25790     type : 'submit',
25791
25792     haveProgress : false,
25793     uploadComplete : false,
25794     
25795     // uploadProgress indicator.
25796     uploadProgress : function()
25797     {
25798         if (!this.form.progressUrl) {
25799             return;
25800         }
25801         
25802         if (!this.haveProgress) {
25803             Roo.MessageBox.progress("Uploading", "Uploading");
25804         }
25805         if (this.uploadComplete) {
25806            Roo.MessageBox.hide();
25807            return;
25808         }
25809         
25810         this.haveProgress = true;
25811    
25812         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25813         
25814         var c = new Roo.data.Connection();
25815         c.request({
25816             url : this.form.progressUrl,
25817             params: {
25818                 id : uid
25819             },
25820             method: 'GET',
25821             success : function(req){
25822                //console.log(data);
25823                 var rdata = false;
25824                 var edata;
25825                 try  {
25826                    rdata = Roo.decode(req.responseText)
25827                 } catch (e) {
25828                     Roo.log("Invalid data from server..");
25829                     Roo.log(edata);
25830                     return;
25831                 }
25832                 if (!rdata || !rdata.success) {
25833                     Roo.log(rdata);
25834                     Roo.MessageBox.alert(Roo.encode(rdata));
25835                     return;
25836                 }
25837                 var data = rdata.data;
25838                 
25839                 if (this.uploadComplete) {
25840                    Roo.MessageBox.hide();
25841                    return;
25842                 }
25843                    
25844                 if (data){
25845                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25846                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25847                     );
25848                 }
25849                 this.uploadProgress.defer(2000,this);
25850             },
25851        
25852             failure: function(data) {
25853                 Roo.log('progress url failed ');
25854                 Roo.log(data);
25855             },
25856             scope : this
25857         });
25858            
25859     },
25860     
25861     
25862     run : function()
25863     {
25864         // run get Values on the form, so it syncs any secondary forms.
25865         this.form.getValues();
25866         
25867         var o = this.options;
25868         var method = this.getMethod();
25869         var isPost = method == 'POST';
25870         if(o.clientValidation === false || this.form.isValid()){
25871             
25872             if (this.form.progressUrl) {
25873                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25874                     (new Date() * 1) + '' + Math.random());
25875                     
25876             } 
25877             
25878             
25879             Roo.Ajax.request(Roo.apply(this.createCallback(), {
25880                 form:this.form.el.dom,
25881                 url:this.getUrl(!isPost),
25882                 method: method,
25883                 params:isPost ? this.getParams() : null,
25884                 isUpload: this.form.fileUpload,
25885                 formData : this.form.formData
25886             }));
25887             
25888             this.uploadProgress();
25889
25890         }else if (o.clientValidation !== false){ // client validation failed
25891             this.failureType = Roo.form.Action.CLIENT_INVALID;
25892             this.form.afterAction(this, false);
25893         }
25894     },
25895
25896     success : function(response)
25897     {
25898         this.uploadComplete= true;
25899         if (this.haveProgress) {
25900             Roo.MessageBox.hide();
25901         }
25902         
25903         
25904         var result = this.processResponse(response);
25905         if(result === true || result.success){
25906             this.form.afterAction(this, true);
25907             return;
25908         }
25909         if(result.errors){
25910             this.form.markInvalid(result.errors);
25911             this.failureType = Roo.form.Action.SERVER_INVALID;
25912         }
25913         this.form.afterAction(this, false);
25914     },
25915     failure : function(response)
25916     {
25917         this.uploadComplete= true;
25918         if (this.haveProgress) {
25919             Roo.MessageBox.hide();
25920         }
25921         
25922         this.response = response;
25923         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25924         this.form.afterAction(this, false);
25925     },
25926     
25927     handleResponse : function(response){
25928         if(this.form.errorReader){
25929             var rs = this.form.errorReader.read(response);
25930             var errors = [];
25931             if(rs.records){
25932                 for(var i = 0, len = rs.records.length; i < len; i++) {
25933                     var r = rs.records[i];
25934                     errors[i] = r.data;
25935                 }
25936             }
25937             if(errors.length < 1){
25938                 errors = null;
25939             }
25940             return {
25941                 success : rs.success,
25942                 errors : errors
25943             };
25944         }
25945         var ret = false;
25946         try {
25947             ret = Roo.decode(response.responseText);
25948         } catch (e) {
25949             ret = {
25950                 success: false,
25951                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
25952                 errors : []
25953             };
25954         }
25955         return ret;
25956         
25957     }
25958 });
25959
25960
25961 Roo.form.Action.Load = function(form, options){
25962     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
25963     this.reader = this.form.reader;
25964 };
25965
25966 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
25967     type : 'load',
25968
25969     run : function(){
25970         
25971         Roo.Ajax.request(Roo.apply(
25972                 this.createCallback(), {
25973                     method:this.getMethod(),
25974                     url:this.getUrl(false),
25975                     params:this.getParams()
25976         }));
25977     },
25978
25979     success : function(response){
25980         
25981         var result = this.processResponse(response);
25982         if(result === true || !result.success || !result.data){
25983             this.failureType = Roo.form.Action.LOAD_FAILURE;
25984             this.form.afterAction(this, false);
25985             return;
25986         }
25987         this.form.clearInvalid();
25988         this.form.setValues(result.data);
25989         this.form.afterAction(this, true);
25990     },
25991
25992     handleResponse : function(response){
25993         if(this.form.reader){
25994             var rs = this.form.reader.read(response);
25995             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
25996             return {
25997                 success : rs.success,
25998                 data : data
25999             };
26000         }
26001         return Roo.decode(response.responseText);
26002     }
26003 });
26004
26005 Roo.form.Action.ACTION_TYPES = {
26006     'load' : Roo.form.Action.Load,
26007     'submit' : Roo.form.Action.Submit
26008 };/*
26009  * Based on:
26010  * Ext JS Library 1.1.1
26011  * Copyright(c) 2006-2007, Ext JS, LLC.
26012  *
26013  * Originally Released Under LGPL - original licence link has changed is not relivant.
26014  *
26015  * Fork - LGPL
26016  * <script type="text/javascript">
26017  */
26018  
26019 /**
26020  * @class Roo.form.Layout
26021  * @extends Roo.Component
26022  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26023  * @constructor
26024  * @param {Object} config Configuration options
26025  */
26026 Roo.form.Layout = function(config){
26027     var xitems = [];
26028     if (config.items) {
26029         xitems = config.items;
26030         delete config.items;
26031     }
26032     Roo.form.Layout.superclass.constructor.call(this, config);
26033     this.stack = [];
26034     Roo.each(xitems, this.addxtype, this);
26035      
26036 };
26037
26038 Roo.extend(Roo.form.Layout, Roo.Component, {
26039     /**
26040      * @cfg {String/Object} autoCreate
26041      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26042      */
26043     /**
26044      * @cfg {String/Object/Function} style
26045      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26046      * a function which returns such a specification.
26047      */
26048     /**
26049      * @cfg {String} labelAlign
26050      * Valid values are "left," "top" and "right" (defaults to "left")
26051      */
26052     /**
26053      * @cfg {Number} labelWidth
26054      * Fixed width in pixels of all field labels (defaults to undefined)
26055      */
26056     /**
26057      * @cfg {Boolean} clear
26058      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26059      */
26060     clear : true,
26061     /**
26062      * @cfg {String} labelSeparator
26063      * The separator to use after field labels (defaults to ':')
26064      */
26065     labelSeparator : ':',
26066     /**
26067      * @cfg {Boolean} hideLabels
26068      * True to suppress the display of field labels in this layout (defaults to false)
26069      */
26070     hideLabels : false,
26071
26072     // private
26073     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26074     
26075     isLayout : true,
26076     
26077     // private
26078     onRender : function(ct, position){
26079         if(this.el){ // from markup
26080             this.el = Roo.get(this.el);
26081         }else {  // generate
26082             var cfg = this.getAutoCreate();
26083             this.el = ct.createChild(cfg, position);
26084         }
26085         if(this.style){
26086             this.el.applyStyles(this.style);
26087         }
26088         if(this.labelAlign){
26089             this.el.addClass('x-form-label-'+this.labelAlign);
26090         }
26091         if(this.hideLabels){
26092             this.labelStyle = "display:none";
26093             this.elementStyle = "padding-left:0;";
26094         }else{
26095             if(typeof this.labelWidth == 'number'){
26096                 this.labelStyle = "width:"+this.labelWidth+"px;";
26097                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26098             }
26099             if(this.labelAlign == 'top'){
26100                 this.labelStyle = "width:auto;";
26101                 this.elementStyle = "padding-left:0;";
26102             }
26103         }
26104         var stack = this.stack;
26105         var slen = stack.length;
26106         if(slen > 0){
26107             if(!this.fieldTpl){
26108                 var t = new Roo.Template(
26109                     '<div class="x-form-item {5}">',
26110                         '<label for="{0}" style="{2}">{1}{4}</label>',
26111                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26112                         '</div>',
26113                     '</div><div class="x-form-clear-left"></div>'
26114                 );
26115                 t.disableFormats = true;
26116                 t.compile();
26117                 Roo.form.Layout.prototype.fieldTpl = t;
26118             }
26119             for(var i = 0; i < slen; i++) {
26120                 if(stack[i].isFormField){
26121                     this.renderField(stack[i]);
26122                 }else{
26123                     this.renderComponent(stack[i]);
26124                 }
26125             }
26126         }
26127         if(this.clear){
26128             this.el.createChild({cls:'x-form-clear'});
26129         }
26130     },
26131
26132     // private
26133     renderField : function(f){
26134         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26135                f.id, //0
26136                f.fieldLabel, //1
26137                f.labelStyle||this.labelStyle||'', //2
26138                this.elementStyle||'', //3
26139                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26140                f.itemCls||this.itemCls||''  //5
26141        ], true).getPrevSibling());
26142     },
26143
26144     // private
26145     renderComponent : function(c){
26146         c.render(c.isLayout ? this.el : this.el.createChild());    
26147     },
26148     /**
26149      * Adds a object form elements (using the xtype property as the factory method.)
26150      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26151      * @param {Object} config 
26152      */
26153     addxtype : function(o)
26154     {
26155         // create the lement.
26156         o.form = this.form;
26157         var fe = Roo.factory(o, Roo.form);
26158         this.form.allItems.push(fe);
26159         this.stack.push(fe);
26160         
26161         if (fe.isFormField) {
26162             this.form.items.add(fe);
26163         }
26164          
26165         return fe;
26166     }
26167 });
26168
26169 /**
26170  * @class Roo.form.Column
26171  * @extends Roo.form.Layout
26172  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26173  * @constructor
26174  * @param {Object} config Configuration options
26175  */
26176 Roo.form.Column = function(config){
26177     Roo.form.Column.superclass.constructor.call(this, config);
26178 };
26179
26180 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26181     /**
26182      * @cfg {Number/String} width
26183      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26184      */
26185     /**
26186      * @cfg {String/Object} autoCreate
26187      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26188      */
26189
26190     // private
26191     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26192
26193     // private
26194     onRender : function(ct, position){
26195         Roo.form.Column.superclass.onRender.call(this, ct, position);
26196         if(this.width){
26197             this.el.setWidth(this.width);
26198         }
26199     }
26200 });
26201
26202
26203 /**
26204  * @class Roo.form.Row
26205  * @extends Roo.form.Layout
26206  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26207  * @constructor
26208  * @param {Object} config Configuration options
26209  */
26210
26211  
26212 Roo.form.Row = function(config){
26213     Roo.form.Row.superclass.constructor.call(this, config);
26214 };
26215  
26216 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26217       /**
26218      * @cfg {Number/String} width
26219      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26220      */
26221     /**
26222      * @cfg {Number/String} height
26223      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26224      */
26225     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26226     
26227     padWidth : 20,
26228     // private
26229     onRender : function(ct, position){
26230         //console.log('row render');
26231         if(!this.rowTpl){
26232             var t = new Roo.Template(
26233                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26234                     '<label for="{0}" style="{2}">{1}{4}</label>',
26235                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26236                     '</div>',
26237                 '</div>'
26238             );
26239             t.disableFormats = true;
26240             t.compile();
26241             Roo.form.Layout.prototype.rowTpl = t;
26242         }
26243         this.fieldTpl = this.rowTpl;
26244         
26245         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26246         var labelWidth = 100;
26247         
26248         if ((this.labelAlign != 'top')) {
26249             if (typeof this.labelWidth == 'number') {
26250                 labelWidth = this.labelWidth
26251             }
26252             this.padWidth =  20 + labelWidth;
26253             
26254         }
26255         
26256         Roo.form.Column.superclass.onRender.call(this, ct, position);
26257         if(this.width){
26258             this.el.setWidth(this.width);
26259         }
26260         if(this.height){
26261             this.el.setHeight(this.height);
26262         }
26263     },
26264     
26265     // private
26266     renderField : function(f){
26267         f.fieldEl = this.fieldTpl.append(this.el, [
26268                f.id, f.fieldLabel,
26269                f.labelStyle||this.labelStyle||'',
26270                this.elementStyle||'',
26271                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26272                f.itemCls||this.itemCls||'',
26273                f.width ? f.width + this.padWidth : 160 + this.padWidth
26274        ],true);
26275     }
26276 });
26277  
26278
26279 /**
26280  * @class Roo.form.FieldSet
26281  * @extends Roo.form.Layout
26282  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26283  * @constructor
26284  * @param {Object} config Configuration options
26285  */
26286 Roo.form.FieldSet = function(config){
26287     Roo.form.FieldSet.superclass.constructor.call(this, config);
26288 };
26289
26290 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26291     /**
26292      * @cfg {String} legend
26293      * The text to display as the legend for the FieldSet (defaults to '')
26294      */
26295     /**
26296      * @cfg {String/Object} autoCreate
26297      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26298      */
26299
26300     // private
26301     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26302
26303     // private
26304     onRender : function(ct, position){
26305         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26306         if(this.legend){
26307             this.setLegend(this.legend);
26308         }
26309     },
26310
26311     // private
26312     setLegend : function(text){
26313         if(this.rendered){
26314             this.el.child('legend').update(text);
26315         }
26316     }
26317 });/*
26318  * Based on:
26319  * Ext JS Library 1.1.1
26320  * Copyright(c) 2006-2007, Ext JS, LLC.
26321  *
26322  * Originally Released Under LGPL - original licence link has changed is not relivant.
26323  *
26324  * Fork - LGPL
26325  * <script type="text/javascript">
26326  */
26327 /**
26328  * @class Roo.form.VTypes
26329  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26330  * @singleton
26331  */
26332 Roo.form.VTypes = function(){
26333     // closure these in so they are only created once.
26334     var alpha = /^[a-zA-Z_]+$/;
26335     var alphanum = /^[a-zA-Z0-9_]+$/;
26336     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26337     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26338
26339     // All these messages and functions are configurable
26340     return {
26341         /**
26342          * The function used to validate email addresses
26343          * @param {String} value The email address
26344          */
26345         'email' : function(v){
26346             return email.test(v);
26347         },
26348         /**
26349          * The error text to display when the email validation function returns false
26350          * @type String
26351          */
26352         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26353         /**
26354          * The keystroke filter mask to be applied on email input
26355          * @type RegExp
26356          */
26357         'emailMask' : /[a-z0-9_\.\-@]/i,
26358
26359         /**
26360          * The function used to validate URLs
26361          * @param {String} value The URL
26362          */
26363         'url' : function(v){
26364             return url.test(v);
26365         },
26366         /**
26367          * The error text to display when the url validation function returns false
26368          * @type String
26369          */
26370         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26371         
26372         /**
26373          * The function used to validate alpha values
26374          * @param {String} value The value
26375          */
26376         'alpha' : function(v){
26377             return alpha.test(v);
26378         },
26379         /**
26380          * The error text to display when the alpha validation function returns false
26381          * @type String
26382          */
26383         'alphaText' : 'This field should only contain letters and _',
26384         /**
26385          * The keystroke filter mask to be applied on alpha input
26386          * @type RegExp
26387          */
26388         'alphaMask' : /[a-z_]/i,
26389
26390         /**
26391          * The function used to validate alphanumeric values
26392          * @param {String} value The value
26393          */
26394         'alphanum' : function(v){
26395             return alphanum.test(v);
26396         },
26397         /**
26398          * The error text to display when the alphanumeric validation function returns false
26399          * @type String
26400          */
26401         'alphanumText' : 'This field should only contain letters, numbers and _',
26402         /**
26403          * The keystroke filter mask to be applied on alphanumeric input
26404          * @type RegExp
26405          */
26406         'alphanumMask' : /[a-z0-9_]/i
26407     };
26408 }();//<script type="text/javascript">
26409
26410 /**
26411  * @class Roo.form.FCKeditor
26412  * @extends Roo.form.TextArea
26413  * Wrapper around the FCKEditor http://www.fckeditor.net
26414  * @constructor
26415  * Creates a new FCKeditor
26416  * @param {Object} config Configuration options
26417  */
26418 Roo.form.FCKeditor = function(config){
26419     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26420     this.addEvents({
26421          /**
26422          * @event editorinit
26423          * Fired when the editor is initialized - you can add extra handlers here..
26424          * @param {FCKeditor} this
26425          * @param {Object} the FCK object.
26426          */
26427         editorinit : true
26428     });
26429     
26430     
26431 };
26432 Roo.form.FCKeditor.editors = { };
26433 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26434 {
26435     //defaultAutoCreate : {
26436     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26437     //},
26438     // private
26439     /**
26440      * @cfg {Object} fck options - see fck manual for details.
26441      */
26442     fckconfig : false,
26443     
26444     /**
26445      * @cfg {Object} fck toolbar set (Basic or Default)
26446      */
26447     toolbarSet : 'Basic',
26448     /**
26449      * @cfg {Object} fck BasePath
26450      */ 
26451     basePath : '/fckeditor/',
26452     
26453     
26454     frame : false,
26455     
26456     value : '',
26457     
26458    
26459     onRender : function(ct, position)
26460     {
26461         if(!this.el){
26462             this.defaultAutoCreate = {
26463                 tag: "textarea",
26464                 style:"width:300px;height:60px;",
26465                 autocomplete: "new-password"
26466             };
26467         }
26468         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26469         /*
26470         if(this.grow){
26471             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26472             if(this.preventScrollbars){
26473                 this.el.setStyle("overflow", "hidden");
26474             }
26475             this.el.setHeight(this.growMin);
26476         }
26477         */
26478         //console.log('onrender' + this.getId() );
26479         Roo.form.FCKeditor.editors[this.getId()] = this;
26480          
26481
26482         this.replaceTextarea() ;
26483         
26484     },
26485     
26486     getEditor : function() {
26487         return this.fckEditor;
26488     },
26489     /**
26490      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26491      * @param {Mixed} value The value to set
26492      */
26493     
26494     
26495     setValue : function(value)
26496     {
26497         //console.log('setValue: ' + value);
26498         
26499         if(typeof(value) == 'undefined') { // not sure why this is happending...
26500             return;
26501         }
26502         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26503         
26504         //if(!this.el || !this.getEditor()) {
26505         //    this.value = value;
26506             //this.setValue.defer(100,this,[value]);    
26507         //    return;
26508         //} 
26509         
26510         if(!this.getEditor()) {
26511             return;
26512         }
26513         
26514         this.getEditor().SetData(value);
26515         
26516         //
26517
26518     },
26519
26520     /**
26521      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26522      * @return {Mixed} value The field value
26523      */
26524     getValue : function()
26525     {
26526         
26527         if (this.frame && this.frame.dom.style.display == 'none') {
26528             return Roo.form.FCKeditor.superclass.getValue.call(this);
26529         }
26530         
26531         if(!this.el || !this.getEditor()) {
26532            
26533            // this.getValue.defer(100,this); 
26534             return this.value;
26535         }
26536        
26537         
26538         var value=this.getEditor().GetData();
26539         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26540         return Roo.form.FCKeditor.superclass.getValue.call(this);
26541         
26542
26543     },
26544
26545     /**
26546      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26547      * @return {Mixed} value The field value
26548      */
26549     getRawValue : function()
26550     {
26551         if (this.frame && this.frame.dom.style.display == 'none') {
26552             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26553         }
26554         
26555         if(!this.el || !this.getEditor()) {
26556             //this.getRawValue.defer(100,this); 
26557             return this.value;
26558             return;
26559         }
26560         
26561         
26562         
26563         var value=this.getEditor().GetData();
26564         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26565         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26566          
26567     },
26568     
26569     setSize : function(w,h) {
26570         
26571         
26572         
26573         //if (this.frame && this.frame.dom.style.display == 'none') {
26574         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26575         //    return;
26576         //}
26577         //if(!this.el || !this.getEditor()) {
26578         //    this.setSize.defer(100,this, [w,h]); 
26579         //    return;
26580         //}
26581         
26582         
26583         
26584         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26585         
26586         this.frame.dom.setAttribute('width', w);
26587         this.frame.dom.setAttribute('height', h);
26588         this.frame.setSize(w,h);
26589         
26590     },
26591     
26592     toggleSourceEdit : function(value) {
26593         
26594       
26595          
26596         this.el.dom.style.display = value ? '' : 'none';
26597         this.frame.dom.style.display = value ?  'none' : '';
26598         
26599     },
26600     
26601     
26602     focus: function(tag)
26603     {
26604         if (this.frame.dom.style.display == 'none') {
26605             return Roo.form.FCKeditor.superclass.focus.call(this);
26606         }
26607         if(!this.el || !this.getEditor()) {
26608             this.focus.defer(100,this, [tag]); 
26609             return;
26610         }
26611         
26612         
26613         
26614         
26615         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26616         this.getEditor().Focus();
26617         if (tgs.length) {
26618             if (!this.getEditor().Selection.GetSelection()) {
26619                 this.focus.defer(100,this, [tag]); 
26620                 return;
26621             }
26622             
26623             
26624             var r = this.getEditor().EditorDocument.createRange();
26625             r.setStart(tgs[0],0);
26626             r.setEnd(tgs[0],0);
26627             this.getEditor().Selection.GetSelection().removeAllRanges();
26628             this.getEditor().Selection.GetSelection().addRange(r);
26629             this.getEditor().Focus();
26630         }
26631         
26632     },
26633     
26634     
26635     
26636     replaceTextarea : function()
26637     {
26638         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26639             return ;
26640         }
26641         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26642         //{
26643             // We must check the elements firstly using the Id and then the name.
26644         var oTextarea = document.getElementById( this.getId() );
26645         
26646         var colElementsByName = document.getElementsByName( this.getId() ) ;
26647          
26648         oTextarea.style.display = 'none' ;
26649
26650         if ( oTextarea.tabIndex ) {            
26651             this.TabIndex = oTextarea.tabIndex ;
26652         }
26653         
26654         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26655         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26656         this.frame = Roo.get(this.getId() + '___Frame')
26657     },
26658     
26659     _getConfigHtml : function()
26660     {
26661         var sConfig = '' ;
26662
26663         for ( var o in this.fckconfig ) {
26664             sConfig += sConfig.length > 0  ? '&amp;' : '';
26665             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26666         }
26667
26668         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26669     },
26670     
26671     
26672     _getIFrameHtml : function()
26673     {
26674         var sFile = 'fckeditor.html' ;
26675         /* no idea what this is about..
26676         try
26677         {
26678             if ( (/fcksource=true/i).test( window.top.location.search ) )
26679                 sFile = 'fckeditor.original.html' ;
26680         }
26681         catch (e) { 
26682         */
26683
26684         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26685         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26686         
26687         
26688         var html = '<iframe id="' + this.getId() +
26689             '___Frame" src="' + sLink +
26690             '" width="' + this.width +
26691             '" height="' + this.height + '"' +
26692             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26693             ' frameborder="0" scrolling="no"></iframe>' ;
26694
26695         return html ;
26696     },
26697     
26698     _insertHtmlBefore : function( html, element )
26699     {
26700         if ( element.insertAdjacentHTML )       {
26701             // IE
26702             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26703         } else { // Gecko
26704             var oRange = document.createRange() ;
26705             oRange.setStartBefore( element ) ;
26706             var oFragment = oRange.createContextualFragment( html );
26707             element.parentNode.insertBefore( oFragment, element ) ;
26708         }
26709     }
26710     
26711     
26712   
26713     
26714     
26715     
26716     
26717
26718 });
26719
26720 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26721
26722 function FCKeditor_OnComplete(editorInstance){
26723     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26724     f.fckEditor = editorInstance;
26725     //console.log("loaded");
26726     f.fireEvent('editorinit', f, editorInstance);
26727
26728   
26729
26730  
26731
26732
26733
26734
26735
26736
26737
26738
26739
26740
26741
26742
26743
26744
26745
26746 //<script type="text/javascript">
26747 /**
26748  * @class Roo.form.GridField
26749  * @extends Roo.form.Field
26750  * Embed a grid (or editable grid into a form)
26751  * STATUS ALPHA
26752  * 
26753  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26754  * it needs 
26755  * xgrid.store = Roo.data.Store
26756  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26757  * xgrid.store.reader = Roo.data.JsonReader 
26758  * 
26759  * 
26760  * @constructor
26761  * Creates a new GridField
26762  * @param {Object} config Configuration options
26763  */
26764 Roo.form.GridField = function(config){
26765     Roo.form.GridField.superclass.constructor.call(this, config);
26766      
26767 };
26768
26769 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26770     /**
26771      * @cfg {Number} width  - used to restrict width of grid..
26772      */
26773     width : 100,
26774     /**
26775      * @cfg {Number} height - used to restrict height of grid..
26776      */
26777     height : 50,
26778      /**
26779      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26780          * 
26781          *}
26782      */
26783     xgrid : false, 
26784     /**
26785      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26786      * {tag: "input", type: "checkbox", autocomplete: "off"})
26787      */
26788    // defaultAutoCreate : { tag: 'div' },
26789     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26790     /**
26791      * @cfg {String} addTitle Text to include for adding a title.
26792      */
26793     addTitle : false,
26794     //
26795     onResize : function(){
26796         Roo.form.Field.superclass.onResize.apply(this, arguments);
26797     },
26798
26799     initEvents : function(){
26800         // Roo.form.Checkbox.superclass.initEvents.call(this);
26801         // has no events...
26802        
26803     },
26804
26805
26806     getResizeEl : function(){
26807         return this.wrap;
26808     },
26809
26810     getPositionEl : function(){
26811         return this.wrap;
26812     },
26813
26814     // private
26815     onRender : function(ct, position){
26816         
26817         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26818         var style = this.style;
26819         delete this.style;
26820         
26821         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26822         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26823         this.viewEl = this.wrap.createChild({ tag: 'div' });
26824         if (style) {
26825             this.viewEl.applyStyles(style);
26826         }
26827         if (this.width) {
26828             this.viewEl.setWidth(this.width);
26829         }
26830         if (this.height) {
26831             this.viewEl.setHeight(this.height);
26832         }
26833         //if(this.inputValue !== undefined){
26834         //this.setValue(this.value);
26835         
26836         
26837         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26838         
26839         
26840         this.grid.render();
26841         this.grid.getDataSource().on('remove', this.refreshValue, this);
26842         this.grid.getDataSource().on('update', this.refreshValue, this);
26843         this.grid.on('afteredit', this.refreshValue, this);
26844  
26845     },
26846      
26847     
26848     /**
26849      * Sets the value of the item. 
26850      * @param {String} either an object  or a string..
26851      */
26852     setValue : function(v){
26853         //this.value = v;
26854         v = v || []; // empty set..
26855         // this does not seem smart - it really only affects memoryproxy grids..
26856         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26857             var ds = this.grid.getDataSource();
26858             // assumes a json reader..
26859             var data = {}
26860             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
26861             ds.loadData( data);
26862         }
26863         // clear selection so it does not get stale.
26864         if (this.grid.sm) { 
26865             this.grid.sm.clearSelections();
26866         }
26867         
26868         Roo.form.GridField.superclass.setValue.call(this, v);
26869         this.refreshValue();
26870         // should load data in the grid really....
26871     },
26872     
26873     // private
26874     refreshValue: function() {
26875          var val = [];
26876         this.grid.getDataSource().each(function(r) {
26877             val.push(r.data);
26878         });
26879         this.el.dom.value = Roo.encode(val);
26880     }
26881     
26882      
26883     
26884     
26885 });/*
26886  * Based on:
26887  * Ext JS Library 1.1.1
26888  * Copyright(c) 2006-2007, Ext JS, LLC.
26889  *
26890  * Originally Released Under LGPL - original licence link has changed is not relivant.
26891  *
26892  * Fork - LGPL
26893  * <script type="text/javascript">
26894  */
26895 /**
26896  * @class Roo.form.DisplayField
26897  * @extends Roo.form.Field
26898  * A generic Field to display non-editable data.
26899  * @cfg {Boolean} closable (true|false) default false
26900  * @constructor
26901  * Creates a new Display Field item.
26902  * @param {Object} config Configuration options
26903  */
26904 Roo.form.DisplayField = function(config){
26905     Roo.form.DisplayField.superclass.constructor.call(this, config);
26906     
26907     this.addEvents({
26908         /**
26909          * @event close
26910          * Fires after the click the close btn
26911              * @param {Roo.form.DisplayField} this
26912              */
26913         close : true
26914     });
26915 };
26916
26917 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
26918     inputType:      'hidden',
26919     allowBlank:     true,
26920     readOnly:         true,
26921     
26922  
26923     /**
26924      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26925      */
26926     focusClass : undefined,
26927     /**
26928      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26929      */
26930     fieldClass: 'x-form-field',
26931     
26932      /**
26933      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
26934      */
26935     valueRenderer: undefined,
26936     
26937     width: 100,
26938     /**
26939      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26940      * {tag: "input", type: "checkbox", autocomplete: "off"})
26941      */
26942      
26943  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
26944  
26945     closable : false,
26946     
26947     onResize : function(){
26948         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
26949         
26950     },
26951
26952     initEvents : function(){
26953         // Roo.form.Checkbox.superclass.initEvents.call(this);
26954         // has no events...
26955         
26956         if(this.closable){
26957             this.closeEl.on('click', this.onClose, this);
26958         }
26959        
26960     },
26961
26962
26963     getResizeEl : function(){
26964         return this.wrap;
26965     },
26966
26967     getPositionEl : function(){
26968         return this.wrap;
26969     },
26970
26971     // private
26972     onRender : function(ct, position){
26973         
26974         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
26975         //if(this.inputValue !== undefined){
26976         this.wrap = this.el.wrap();
26977         
26978         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
26979         
26980         if(this.closable){
26981             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
26982         }
26983         
26984         if (this.bodyStyle) {
26985             this.viewEl.applyStyles(this.bodyStyle);
26986         }
26987         //this.viewEl.setStyle('padding', '2px');
26988         
26989         this.setValue(this.value);
26990         
26991     },
26992 /*
26993     // private
26994     initValue : Roo.emptyFn,
26995
26996   */
26997
26998         // private
26999     onClick : function(){
27000         
27001     },
27002
27003     /**
27004      * Sets the checked state of the checkbox.
27005      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27006      */
27007     setValue : function(v){
27008         this.value = v;
27009         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27010         // this might be called before we have a dom element..
27011         if (!this.viewEl) {
27012             return;
27013         }
27014         this.viewEl.dom.innerHTML = html;
27015         Roo.form.DisplayField.superclass.setValue.call(this, v);
27016
27017     },
27018     
27019     onClose : function(e)
27020     {
27021         e.preventDefault();
27022         
27023         this.fireEvent('close', this);
27024     }
27025 });/*
27026  * 
27027  * Licence- LGPL
27028  * 
27029  */
27030
27031 /**
27032  * @class Roo.form.DayPicker
27033  * @extends Roo.form.Field
27034  * A Day picker show [M] [T] [W] ....
27035  * @constructor
27036  * Creates a new Day Picker
27037  * @param {Object} config Configuration options
27038  */
27039 Roo.form.DayPicker= function(config){
27040     Roo.form.DayPicker.superclass.constructor.call(this, config);
27041      
27042 };
27043
27044 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27045     /**
27046      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27047      */
27048     focusClass : undefined,
27049     /**
27050      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27051      */
27052     fieldClass: "x-form-field",
27053    
27054     /**
27055      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27056      * {tag: "input", type: "checkbox", autocomplete: "off"})
27057      */
27058     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27059     
27060    
27061     actionMode : 'viewEl', 
27062     //
27063     // private
27064  
27065     inputType : 'hidden',
27066     
27067      
27068     inputElement: false, // real input element?
27069     basedOn: false, // ????
27070     
27071     isFormField: true, // not sure where this is needed!!!!
27072
27073     onResize : function(){
27074         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27075         if(!this.boxLabel){
27076             this.el.alignTo(this.wrap, 'c-c');
27077         }
27078     },
27079
27080     initEvents : function(){
27081         Roo.form.Checkbox.superclass.initEvents.call(this);
27082         this.el.on("click", this.onClick,  this);
27083         this.el.on("change", this.onClick,  this);
27084     },
27085
27086
27087     getResizeEl : function(){
27088         return this.wrap;
27089     },
27090
27091     getPositionEl : function(){
27092         return this.wrap;
27093     },
27094
27095     
27096     // private
27097     onRender : function(ct, position){
27098         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27099        
27100         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27101         
27102         var r1 = '<table><tr>';
27103         var r2 = '<tr class="x-form-daypick-icons">';
27104         for (var i=0; i < 7; i++) {
27105             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27106             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27107         }
27108         
27109         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27110         viewEl.select('img').on('click', this.onClick, this);
27111         this.viewEl = viewEl;   
27112         
27113         
27114         // this will not work on Chrome!!!
27115         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27116         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27117         
27118         
27119           
27120
27121     },
27122
27123     // private
27124     initValue : Roo.emptyFn,
27125
27126     /**
27127      * Returns the checked state of the checkbox.
27128      * @return {Boolean} True if checked, else false
27129      */
27130     getValue : function(){
27131         return this.el.dom.value;
27132         
27133     },
27134
27135         // private
27136     onClick : function(e){ 
27137         //this.setChecked(!this.checked);
27138         Roo.get(e.target).toggleClass('x-menu-item-checked');
27139         this.refreshValue();
27140         //if(this.el.dom.checked != this.checked){
27141         //    this.setValue(this.el.dom.checked);
27142        // }
27143     },
27144     
27145     // private
27146     refreshValue : function()
27147     {
27148         var val = '';
27149         this.viewEl.select('img',true).each(function(e,i,n)  {
27150             val += e.is(".x-menu-item-checked") ? String(n) : '';
27151         });
27152         this.setValue(val, true);
27153     },
27154
27155     /**
27156      * Sets the checked state of the checkbox.
27157      * On is always based on a string comparison between inputValue and the param.
27158      * @param {Boolean/String} value - the value to set 
27159      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27160      */
27161     setValue : function(v,suppressEvent){
27162         if (!this.el.dom) {
27163             return;
27164         }
27165         var old = this.el.dom.value ;
27166         this.el.dom.value = v;
27167         if (suppressEvent) {
27168             return ;
27169         }
27170          
27171         // update display..
27172         this.viewEl.select('img',true).each(function(e,i,n)  {
27173             
27174             var on = e.is(".x-menu-item-checked");
27175             var newv = v.indexOf(String(n)) > -1;
27176             if (on != newv) {
27177                 e.toggleClass('x-menu-item-checked');
27178             }
27179             
27180         });
27181         
27182         
27183         this.fireEvent('change', this, v, old);
27184         
27185         
27186     },
27187    
27188     // handle setting of hidden value by some other method!!?!?
27189     setFromHidden: function()
27190     {
27191         if(!this.el){
27192             return;
27193         }
27194         //console.log("SET FROM HIDDEN");
27195         //alert('setFrom hidden');
27196         this.setValue(this.el.dom.value);
27197     },
27198     
27199     onDestroy : function()
27200     {
27201         if(this.viewEl){
27202             Roo.get(this.viewEl).remove();
27203         }
27204          
27205         Roo.form.DayPicker.superclass.onDestroy.call(this);
27206     }
27207
27208 });/*
27209  * RooJS Library 1.1.1
27210  * Copyright(c) 2008-2011  Alan Knowles
27211  *
27212  * License - LGPL
27213  */
27214  
27215
27216 /**
27217  * @class Roo.form.ComboCheck
27218  * @extends Roo.form.ComboBox
27219  * A combobox for multiple select items.
27220  *
27221  * FIXME - could do with a reset button..
27222  * 
27223  * @constructor
27224  * Create a new ComboCheck
27225  * @param {Object} config Configuration options
27226  */
27227 Roo.form.ComboCheck = function(config){
27228     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27229     // should verify some data...
27230     // like
27231     // hiddenName = required..
27232     // displayField = required
27233     // valudField == required
27234     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27235     var _t = this;
27236     Roo.each(req, function(e) {
27237         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27238             throw "Roo.form.ComboCheck : missing value for: " + e;
27239         }
27240     });
27241     
27242     
27243 };
27244
27245 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27246      
27247      
27248     editable : false,
27249      
27250     selectedClass: 'x-menu-item-checked', 
27251     
27252     // private
27253     onRender : function(ct, position){
27254         var _t = this;
27255         
27256         
27257         
27258         if(!this.tpl){
27259             var cls = 'x-combo-list';
27260
27261             
27262             this.tpl =  new Roo.Template({
27263                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27264                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27265                    '<span>{' + this.displayField + '}</span>' +
27266                     '</div>' 
27267                 
27268             });
27269         }
27270  
27271         
27272         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27273         this.view.singleSelect = false;
27274         this.view.multiSelect = true;
27275         this.view.toggleSelect = true;
27276         this.pageTb.add(new Roo.Toolbar.Fill(), {
27277             
27278             text: 'Done',
27279             handler: function()
27280             {
27281                 _t.collapse();
27282             }
27283         });
27284     },
27285     
27286     onViewOver : function(e, t){
27287         // do nothing...
27288         return;
27289         
27290     },
27291     
27292     onViewClick : function(doFocus,index){
27293         return;
27294         
27295     },
27296     select: function () {
27297         //Roo.log("SELECT CALLED");
27298     },
27299      
27300     selectByValue : function(xv, scrollIntoView){
27301         var ar = this.getValueArray();
27302         var sels = [];
27303         
27304         Roo.each(ar, function(v) {
27305             if(v === undefined || v === null){
27306                 return;
27307             }
27308             var r = this.findRecord(this.valueField, v);
27309             if(r){
27310                 sels.push(this.store.indexOf(r))
27311                 
27312             }
27313         },this);
27314         this.view.select(sels);
27315         return false;
27316     },
27317     
27318     
27319     
27320     onSelect : function(record, index){
27321        // Roo.log("onselect Called");
27322        // this is only called by the clear button now..
27323         this.view.clearSelections();
27324         this.setValue('[]');
27325         if (this.value != this.valueBefore) {
27326             this.fireEvent('change', this, this.value, this.valueBefore);
27327             this.valueBefore = this.value;
27328         }
27329     },
27330     getValueArray : function()
27331     {
27332         var ar = [] ;
27333         
27334         try {
27335             //Roo.log(this.value);
27336             if (typeof(this.value) == 'undefined') {
27337                 return [];
27338             }
27339             var ar = Roo.decode(this.value);
27340             return  ar instanceof Array ? ar : []; //?? valid?
27341             
27342         } catch(e) {
27343             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27344             return [];
27345         }
27346          
27347     },
27348     expand : function ()
27349     {
27350         
27351         Roo.form.ComboCheck.superclass.expand.call(this);
27352         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27353         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27354         
27355
27356     },
27357     
27358     collapse : function(){
27359         Roo.form.ComboCheck.superclass.collapse.call(this);
27360         var sl = this.view.getSelectedIndexes();
27361         var st = this.store;
27362         var nv = [];
27363         var tv = [];
27364         var r;
27365         Roo.each(sl, function(i) {
27366             r = st.getAt(i);
27367             nv.push(r.get(this.valueField));
27368         },this);
27369         this.setValue(Roo.encode(nv));
27370         if (this.value != this.valueBefore) {
27371
27372             this.fireEvent('change', this, this.value, this.valueBefore);
27373             this.valueBefore = this.value;
27374         }
27375         
27376     },
27377     
27378     setValue : function(v){
27379         // Roo.log(v);
27380         this.value = v;
27381         
27382         var vals = this.getValueArray();
27383         var tv = [];
27384         Roo.each(vals, function(k) {
27385             var r = this.findRecord(this.valueField, k);
27386             if(r){
27387                 tv.push(r.data[this.displayField]);
27388             }else if(this.valueNotFoundText !== undefined){
27389                 tv.push( this.valueNotFoundText );
27390             }
27391         },this);
27392        // Roo.log(tv);
27393         
27394         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27395         this.hiddenField.value = v;
27396         this.value = v;
27397     }
27398     
27399 });/*
27400  * Based on:
27401  * Ext JS Library 1.1.1
27402  * Copyright(c) 2006-2007, Ext JS, LLC.
27403  *
27404  * Originally Released Under LGPL - original licence link has changed is not relivant.
27405  *
27406  * Fork - LGPL
27407  * <script type="text/javascript">
27408  */
27409  
27410 /**
27411  * @class Roo.form.Signature
27412  * @extends Roo.form.Field
27413  * Signature field.  
27414  * @constructor
27415  * 
27416  * @param {Object} config Configuration options
27417  */
27418
27419 Roo.form.Signature = function(config){
27420     Roo.form.Signature.superclass.constructor.call(this, config);
27421     
27422     this.addEvents({// not in used??
27423          /**
27424          * @event confirm
27425          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27426              * @param {Roo.form.Signature} combo This combo box
27427              */
27428         'confirm' : true,
27429         /**
27430          * @event reset
27431          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27432              * @param {Roo.form.ComboBox} combo This combo box
27433              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27434              */
27435         'reset' : true
27436     });
27437 };
27438
27439 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27440     /**
27441      * @cfg {Object} labels Label to use when rendering a form.
27442      * defaults to 
27443      * labels : { 
27444      *      clear : "Clear",
27445      *      confirm : "Confirm"
27446      *  }
27447      */
27448     labels : { 
27449         clear : "Clear",
27450         confirm : "Confirm"
27451     },
27452     /**
27453      * @cfg {Number} width The signature panel width (defaults to 300)
27454      */
27455     width: 300,
27456     /**
27457      * @cfg {Number} height The signature panel height (defaults to 100)
27458      */
27459     height : 100,
27460     /**
27461      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27462      */
27463     allowBlank : false,
27464     
27465     //private
27466     // {Object} signPanel The signature SVG panel element (defaults to {})
27467     signPanel : {},
27468     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27469     isMouseDown : false,
27470     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27471     isConfirmed : false,
27472     // {String} signatureTmp SVG mapping string (defaults to empty string)
27473     signatureTmp : '',
27474     
27475     
27476     defaultAutoCreate : { // modified by initCompnoent..
27477         tag: "input",
27478         type:"hidden"
27479     },
27480
27481     // private
27482     onRender : function(ct, position){
27483         
27484         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27485         
27486         this.wrap = this.el.wrap({
27487             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27488         });
27489         
27490         this.createToolbar(this);
27491         this.signPanel = this.wrap.createChild({
27492                 tag: 'div',
27493                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27494             }, this.el
27495         );
27496             
27497         this.svgID = Roo.id();
27498         this.svgEl = this.signPanel.createChild({
27499               xmlns : 'http://www.w3.org/2000/svg',
27500               tag : 'svg',
27501               id : this.svgID + "-svg",
27502               width: this.width,
27503               height: this.height,
27504               viewBox: '0 0 '+this.width+' '+this.height,
27505               cn : [
27506                 {
27507                     tag: "rect",
27508                     id: this.svgID + "-svg-r",
27509                     width: this.width,
27510                     height: this.height,
27511                     fill: "#ffa"
27512                 },
27513                 {
27514                     tag: "line",
27515                     id: this.svgID + "-svg-l",
27516                     x1: "0", // start
27517                     y1: (this.height*0.8), // start set the line in 80% of height
27518                     x2: this.width, // end
27519                     y2: (this.height*0.8), // end set the line in 80% of height
27520                     'stroke': "#666",
27521                     'stroke-width': "1",
27522                     'stroke-dasharray': "3",
27523                     'shape-rendering': "crispEdges",
27524                     'pointer-events': "none"
27525                 },
27526                 {
27527                     tag: "path",
27528                     id: this.svgID + "-svg-p",
27529                     'stroke': "navy",
27530                     'stroke-width': "3",
27531                     'fill': "none",
27532                     'pointer-events': 'none'
27533                 }
27534               ]
27535         });
27536         this.createSVG();
27537         this.svgBox = this.svgEl.dom.getScreenCTM();
27538     },
27539     createSVG : function(){ 
27540         var svg = this.signPanel;
27541         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27542         var t = this;
27543
27544         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27545         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27546         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27547         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27548         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27549         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27550         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27551         
27552     },
27553     isTouchEvent : function(e){
27554         return e.type.match(/^touch/);
27555     },
27556     getCoords : function (e) {
27557         var pt    = this.svgEl.dom.createSVGPoint();
27558         pt.x = e.clientX; 
27559         pt.y = e.clientY;
27560         if (this.isTouchEvent(e)) {
27561             pt.x =  e.targetTouches[0].clientX;
27562             pt.y = e.targetTouches[0].clientY;
27563         }
27564         var a = this.svgEl.dom.getScreenCTM();
27565         var b = a.inverse();
27566         var mx = pt.matrixTransform(b);
27567         return mx.x + ',' + mx.y;
27568     },
27569     //mouse event headler 
27570     down : function (e) {
27571         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27572         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27573         
27574         this.isMouseDown = true;
27575         
27576         e.preventDefault();
27577     },
27578     move : function (e) {
27579         if (this.isMouseDown) {
27580             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27581             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27582         }
27583         
27584         e.preventDefault();
27585     },
27586     up : function (e) {
27587         this.isMouseDown = false;
27588         var sp = this.signatureTmp.split(' ');
27589         
27590         if(sp.length > 1){
27591             if(!sp[sp.length-2].match(/^L/)){
27592                 sp.pop();
27593                 sp.pop();
27594                 sp.push("");
27595                 this.signatureTmp = sp.join(" ");
27596             }
27597         }
27598         if(this.getValue() != this.signatureTmp){
27599             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27600             this.isConfirmed = false;
27601         }
27602         e.preventDefault();
27603     },
27604     
27605     /**
27606      * Protected method that will not generally be called directly. It
27607      * is called when the editor creates its toolbar. Override this method if you need to
27608      * add custom toolbar buttons.
27609      * @param {HtmlEditor} editor
27610      */
27611     createToolbar : function(editor){
27612          function btn(id, toggle, handler){
27613             var xid = fid + '-'+ id ;
27614             return {
27615                 id : xid,
27616                 cmd : id,
27617                 cls : 'x-btn-icon x-edit-'+id,
27618                 enableToggle:toggle !== false,
27619                 scope: editor, // was editor...
27620                 handler:handler||editor.relayBtnCmd,
27621                 clickEvent:'mousedown',
27622                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27623                 tabIndex:-1
27624             };
27625         }
27626         
27627         
27628         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27629         this.tb = tb;
27630         this.tb.add(
27631            {
27632                 cls : ' x-signature-btn x-signature-'+id,
27633                 scope: editor, // was editor...
27634                 handler: this.reset,
27635                 clickEvent:'mousedown',
27636                 text: this.labels.clear
27637             },
27638             {
27639                  xtype : 'Fill',
27640                  xns: Roo.Toolbar
27641             }, 
27642             {
27643                 cls : '  x-signature-btn x-signature-'+id,
27644                 scope: editor, // was editor...
27645                 handler: this.confirmHandler,
27646                 clickEvent:'mousedown',
27647                 text: this.labels.confirm
27648             }
27649         );
27650     
27651     },
27652     //public
27653     /**
27654      * when user is clicked confirm then show this image.....
27655      * 
27656      * @return {String} Image Data URI
27657      */
27658     getImageDataURI : function(){
27659         var svg = this.svgEl.dom.parentNode.innerHTML;
27660         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27661         return src; 
27662     },
27663     /**
27664      * 
27665      * @return {Boolean} this.isConfirmed
27666      */
27667     getConfirmed : function(){
27668         return this.isConfirmed;
27669     },
27670     /**
27671      * 
27672      * @return {Number} this.width
27673      */
27674     getWidth : function(){
27675         return this.width;
27676     },
27677     /**
27678      * 
27679      * @return {Number} this.height
27680      */
27681     getHeight : function(){
27682         return this.height;
27683     },
27684     // private
27685     getSignature : function(){
27686         return this.signatureTmp;
27687     },
27688     // private
27689     reset : function(){
27690         this.signatureTmp = '';
27691         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27692         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27693         this.isConfirmed = false;
27694         Roo.form.Signature.superclass.reset.call(this);
27695     },
27696     setSignature : function(s){
27697         this.signatureTmp = s;
27698         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27699         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27700         this.setValue(s);
27701         this.isConfirmed = false;
27702         Roo.form.Signature.superclass.reset.call(this);
27703     }, 
27704     test : function(){
27705 //        Roo.log(this.signPanel.dom.contentWindow.up())
27706     },
27707     //private
27708     setConfirmed : function(){
27709         
27710         
27711         
27712 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27713     },
27714     // private
27715     confirmHandler : function(){
27716         if(!this.getSignature()){
27717             return;
27718         }
27719         
27720         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27721         this.setValue(this.getSignature());
27722         this.isConfirmed = true;
27723         
27724         this.fireEvent('confirm', this);
27725     },
27726     // private
27727     // Subclasses should provide the validation implementation by overriding this
27728     validateValue : function(value){
27729         if(this.allowBlank){
27730             return true;
27731         }
27732         
27733         if(this.isConfirmed){
27734             return true;
27735         }
27736         return false;
27737     }
27738 });/*
27739  * Based on:
27740  * Ext JS Library 1.1.1
27741  * Copyright(c) 2006-2007, Ext JS, LLC.
27742  *
27743  * Originally Released Under LGPL - original licence link has changed is not relivant.
27744  *
27745  * Fork - LGPL
27746  * <script type="text/javascript">
27747  */
27748  
27749
27750 /**
27751  * @class Roo.form.ComboBox
27752  * @extends Roo.form.TriggerField
27753  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27754  * @constructor
27755  * Create a new ComboBox.
27756  * @param {Object} config Configuration options
27757  */
27758 Roo.form.Select = function(config){
27759     Roo.form.Select.superclass.constructor.call(this, config);
27760      
27761 };
27762
27763 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27764     /**
27765      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27766      */
27767     /**
27768      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27769      * rendering into an Roo.Editor, defaults to false)
27770      */
27771     /**
27772      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27773      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27774      */
27775     /**
27776      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27777      */
27778     /**
27779      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27780      * the dropdown list (defaults to undefined, with no header element)
27781      */
27782
27783      /**
27784      * @cfg {String/Roo.Template} tpl The template to use to render the output
27785      */
27786      
27787     // private
27788     defaultAutoCreate : {tag: "select"  },
27789     /**
27790      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27791      */
27792     listWidth: undefined,
27793     /**
27794      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27795      * mode = 'remote' or 'text' if mode = 'local')
27796      */
27797     displayField: undefined,
27798     /**
27799      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27800      * mode = 'remote' or 'value' if mode = 'local'). 
27801      * Note: use of a valueField requires the user make a selection
27802      * in order for a value to be mapped.
27803      */
27804     valueField: undefined,
27805     
27806     
27807     /**
27808      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27809      * field's data value (defaults to the underlying DOM element's name)
27810      */
27811     hiddenName: undefined,
27812     /**
27813      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27814      */
27815     listClass: '',
27816     /**
27817      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27818      */
27819     selectedClass: 'x-combo-selected',
27820     /**
27821      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27822      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27823      * which displays a downward arrow icon).
27824      */
27825     triggerClass : 'x-form-arrow-trigger',
27826     /**
27827      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27828      */
27829     shadow:'sides',
27830     /**
27831      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27832      * anchor positions (defaults to 'tl-bl')
27833      */
27834     listAlign: 'tl-bl?',
27835     /**
27836      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27837      */
27838     maxHeight: 300,
27839     /**
27840      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27841      * query specified by the allQuery config option (defaults to 'query')
27842      */
27843     triggerAction: 'query',
27844     /**
27845      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27846      * (defaults to 4, does not apply if editable = false)
27847      */
27848     minChars : 4,
27849     /**
27850      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27851      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27852      */
27853     typeAhead: false,
27854     /**
27855      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27856      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27857      */
27858     queryDelay: 500,
27859     /**
27860      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27861      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
27862      */
27863     pageSize: 0,
27864     /**
27865      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
27866      * when editable = true (defaults to false)
27867      */
27868     selectOnFocus:false,
27869     /**
27870      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27871      */
27872     queryParam: 'query',
27873     /**
27874      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
27875      * when mode = 'remote' (defaults to 'Loading...')
27876      */
27877     loadingText: 'Loading...',
27878     /**
27879      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27880      */
27881     resizable: false,
27882     /**
27883      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27884      */
27885     handleHeight : 8,
27886     /**
27887      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27888      * traditional select (defaults to true)
27889      */
27890     editable: true,
27891     /**
27892      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27893      */
27894     allQuery: '',
27895     /**
27896      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27897      */
27898     mode: 'remote',
27899     /**
27900      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27901      * listWidth has a higher value)
27902      */
27903     minListWidth : 70,
27904     /**
27905      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
27906      * allow the user to set arbitrary text into the field (defaults to false)
27907      */
27908     forceSelection:false,
27909     /**
27910      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
27911      * if typeAhead = true (defaults to 250)
27912      */
27913     typeAheadDelay : 250,
27914     /**
27915      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
27916      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
27917      */
27918     valueNotFoundText : undefined,
27919     
27920     /**
27921      * @cfg {String} defaultValue The value displayed after loading the store.
27922      */
27923     defaultValue: '',
27924     
27925     /**
27926      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
27927      */
27928     blockFocus : false,
27929     
27930     /**
27931      * @cfg {Boolean} disableClear Disable showing of clear button.
27932      */
27933     disableClear : false,
27934     /**
27935      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
27936      */
27937     alwaysQuery : false,
27938     
27939     //private
27940     addicon : false,
27941     editicon: false,
27942     
27943     // element that contains real text value.. (when hidden is used..)
27944      
27945     // private
27946     onRender : function(ct, position){
27947         Roo.form.Field.prototype.onRender.call(this, ct, position);
27948         
27949         if(this.store){
27950             this.store.on('beforeload', this.onBeforeLoad, this);
27951             this.store.on('load', this.onLoad, this);
27952             this.store.on('loadexception', this.onLoadException, this);
27953             this.store.load({});
27954         }
27955         
27956         
27957         
27958     },
27959
27960     // private
27961     initEvents : function(){
27962         //Roo.form.ComboBox.superclass.initEvents.call(this);
27963  
27964     },
27965
27966     onDestroy : function(){
27967        
27968         if(this.store){
27969             this.store.un('beforeload', this.onBeforeLoad, this);
27970             this.store.un('load', this.onLoad, this);
27971             this.store.un('loadexception', this.onLoadException, this);
27972         }
27973         //Roo.form.ComboBox.superclass.onDestroy.call(this);
27974     },
27975
27976     // private
27977     fireKey : function(e){
27978         if(e.isNavKeyPress() && !this.list.isVisible()){
27979             this.fireEvent("specialkey", this, e);
27980         }
27981     },
27982
27983     // private
27984     onResize: function(w, h){
27985         
27986         return; 
27987     
27988         
27989     },
27990
27991     /**
27992      * Allow or prevent the user from directly editing the field text.  If false is passed,
27993      * the user will only be able to select from the items defined in the dropdown list.  This method
27994      * is the runtime equivalent of setting the 'editable' config option at config time.
27995      * @param {Boolean} value True to allow the user to directly edit the field text
27996      */
27997     setEditable : function(value){
27998          
27999     },
28000
28001     // private
28002     onBeforeLoad : function(){
28003         
28004         Roo.log("Select before load");
28005         return;
28006     
28007         this.innerList.update(this.loadingText ?
28008                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28009         //this.restrictHeight();
28010         this.selectedIndex = -1;
28011     },
28012
28013     // private
28014     onLoad : function(){
28015
28016     
28017         var dom = this.el.dom;
28018         dom.innerHTML = '';
28019          var od = dom.ownerDocument;
28020          
28021         if (this.emptyText) {
28022             var op = od.createElement('option');
28023             op.setAttribute('value', '');
28024             op.innerHTML = String.format('{0}', this.emptyText);
28025             dom.appendChild(op);
28026         }
28027         if(this.store.getCount() > 0){
28028            
28029             var vf = this.valueField;
28030             var df = this.displayField;
28031             this.store.data.each(function(r) {
28032                 // which colmsn to use... testing - cdoe / title..
28033                 var op = od.createElement('option');
28034                 op.setAttribute('value', r.data[vf]);
28035                 op.innerHTML = String.format('{0}', r.data[df]);
28036                 dom.appendChild(op);
28037             });
28038             if (typeof(this.defaultValue != 'undefined')) {
28039                 this.setValue(this.defaultValue);
28040             }
28041             
28042              
28043         }else{
28044             //this.onEmptyResults();
28045         }
28046         //this.el.focus();
28047     },
28048     // private
28049     onLoadException : function()
28050     {
28051         dom.innerHTML = '';
28052             
28053         Roo.log("Select on load exception");
28054         return;
28055     
28056         this.collapse();
28057         Roo.log(this.store.reader.jsonData);
28058         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28059             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28060         }
28061         
28062         
28063     },
28064     // private
28065     onTypeAhead : function(){
28066          
28067     },
28068
28069     // private
28070     onSelect : function(record, index){
28071         Roo.log('on select?');
28072         return;
28073         if(this.fireEvent('beforeselect', this, record, index) !== false){
28074             this.setFromData(index > -1 ? record.data : false);
28075             this.collapse();
28076             this.fireEvent('select', this, record, index);
28077         }
28078     },
28079
28080     /**
28081      * Returns the currently selected field value or empty string if no value is set.
28082      * @return {String} value The selected value
28083      */
28084     getValue : function(){
28085         var dom = this.el.dom;
28086         this.value = dom.options[dom.selectedIndex].value;
28087         return this.value;
28088         
28089     },
28090
28091     /**
28092      * Clears any text/value currently set in the field
28093      */
28094     clearValue : function(){
28095         this.value = '';
28096         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28097         
28098     },
28099
28100     /**
28101      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28102      * will be displayed in the field.  If the value does not match the data value of an existing item,
28103      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28104      * Otherwise the field will be blank (although the value will still be set).
28105      * @param {String} value The value to match
28106      */
28107     setValue : function(v){
28108         var d = this.el.dom;
28109         for (var i =0; i < d.options.length;i++) {
28110             if (v == d.options[i].value) {
28111                 d.selectedIndex = i;
28112                 this.value = v;
28113                 return;
28114             }
28115         }
28116         this.clearValue();
28117     },
28118     /**
28119      * @property {Object} the last set data for the element
28120      */
28121     
28122     lastData : false,
28123     /**
28124      * Sets the value of the field based on a object which is related to the record format for the store.
28125      * @param {Object} value the value to set as. or false on reset?
28126      */
28127     setFromData : function(o){
28128         Roo.log('setfrom data?');
28129          
28130         
28131         
28132     },
28133     // private
28134     reset : function(){
28135         this.clearValue();
28136     },
28137     // private
28138     findRecord : function(prop, value){
28139         
28140         return false;
28141     
28142         var record;
28143         if(this.store.getCount() > 0){
28144             this.store.each(function(r){
28145                 if(r.data[prop] == value){
28146                     record = r;
28147                     return false;
28148                 }
28149                 return true;
28150             });
28151         }
28152         return record;
28153     },
28154     
28155     getName: function()
28156     {
28157         // returns hidden if it's set..
28158         if (!this.rendered) {return ''};
28159         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28160         
28161     },
28162      
28163
28164     
28165
28166     // private
28167     onEmptyResults : function(){
28168         Roo.log('empty results');
28169         //this.collapse();
28170     },
28171
28172     /**
28173      * Returns true if the dropdown list is expanded, else false.
28174      */
28175     isExpanded : function(){
28176         return false;
28177     },
28178
28179     /**
28180      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28181      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28182      * @param {String} value The data value of the item to select
28183      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28184      * selected item if it is not currently in view (defaults to true)
28185      * @return {Boolean} True if the value matched an item in the list, else false
28186      */
28187     selectByValue : function(v, scrollIntoView){
28188         Roo.log('select By Value');
28189         return false;
28190     
28191         if(v !== undefined && v !== null){
28192             var r = this.findRecord(this.valueField || this.displayField, v);
28193             if(r){
28194                 this.select(this.store.indexOf(r), scrollIntoView);
28195                 return true;
28196             }
28197         }
28198         return false;
28199     },
28200
28201     /**
28202      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28203      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28204      * @param {Number} index The zero-based index of the list item to select
28205      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28206      * selected item if it is not currently in view (defaults to true)
28207      */
28208     select : function(index, scrollIntoView){
28209         Roo.log('select ');
28210         return  ;
28211         
28212         this.selectedIndex = index;
28213         this.view.select(index);
28214         if(scrollIntoView !== false){
28215             var el = this.view.getNode(index);
28216             if(el){
28217                 this.innerList.scrollChildIntoView(el, false);
28218             }
28219         }
28220     },
28221
28222       
28223
28224     // private
28225     validateBlur : function(){
28226         
28227         return;
28228         
28229     },
28230
28231     // private
28232     initQuery : function(){
28233         this.doQuery(this.getRawValue());
28234     },
28235
28236     // private
28237     doForce : function(){
28238         if(this.el.dom.value.length > 0){
28239             this.el.dom.value =
28240                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28241              
28242         }
28243     },
28244
28245     /**
28246      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28247      * query allowing the query action to be canceled if needed.
28248      * @param {String} query The SQL query to execute
28249      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28250      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28251      * saved in the current store (defaults to false)
28252      */
28253     doQuery : function(q, forceAll){
28254         
28255         Roo.log('doQuery?');
28256         if(q === undefined || q === null){
28257             q = '';
28258         }
28259         var qe = {
28260             query: q,
28261             forceAll: forceAll,
28262             combo: this,
28263             cancel:false
28264         };
28265         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28266             return false;
28267         }
28268         q = qe.query;
28269         forceAll = qe.forceAll;
28270         if(forceAll === true || (q.length >= this.minChars)){
28271             if(this.lastQuery != q || this.alwaysQuery){
28272                 this.lastQuery = q;
28273                 if(this.mode == 'local'){
28274                     this.selectedIndex = -1;
28275                     if(forceAll){
28276                         this.store.clearFilter();
28277                     }else{
28278                         this.store.filter(this.displayField, q);
28279                     }
28280                     this.onLoad();
28281                 }else{
28282                     this.store.baseParams[this.queryParam] = q;
28283                     this.store.load({
28284                         params: this.getParams(q)
28285                     });
28286                     this.expand();
28287                 }
28288             }else{
28289                 this.selectedIndex = -1;
28290                 this.onLoad();   
28291             }
28292         }
28293     },
28294
28295     // private
28296     getParams : function(q){
28297         var p = {};
28298         //p[this.queryParam] = q;
28299         if(this.pageSize){
28300             p.start = 0;
28301             p.limit = this.pageSize;
28302         }
28303         return p;
28304     },
28305
28306     /**
28307      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28308      */
28309     collapse : function(){
28310         
28311     },
28312
28313     // private
28314     collapseIf : function(e){
28315         
28316     },
28317
28318     /**
28319      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28320      */
28321     expand : function(){
28322         
28323     } ,
28324
28325     // private
28326      
28327
28328     /** 
28329     * @cfg {Boolean} grow 
28330     * @hide 
28331     */
28332     /** 
28333     * @cfg {Number} growMin 
28334     * @hide 
28335     */
28336     /** 
28337     * @cfg {Number} growMax 
28338     * @hide 
28339     */
28340     /**
28341      * @hide
28342      * @method autoSize
28343      */
28344     
28345     setWidth : function()
28346     {
28347         
28348     },
28349     getResizeEl : function(){
28350         return this.el;
28351     }
28352 });//<script type="text/javasscript">
28353  
28354
28355 /**
28356  * @class Roo.DDView
28357  * A DnD enabled version of Roo.View.
28358  * @param {Element/String} container The Element in which to create the View.
28359  * @param {String} tpl The template string used to create the markup for each element of the View
28360  * @param {Object} config The configuration properties. These include all the config options of
28361  * {@link Roo.View} plus some specific to this class.<br>
28362  * <p>
28363  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28364  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28365  * <p>
28366  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28367 .x-view-drag-insert-above {
28368         border-top:1px dotted #3366cc;
28369 }
28370 .x-view-drag-insert-below {
28371         border-bottom:1px dotted #3366cc;
28372 }
28373 </code></pre>
28374  * 
28375  */
28376  
28377 Roo.DDView = function(container, tpl, config) {
28378     Roo.DDView.superclass.constructor.apply(this, arguments);
28379     this.getEl().setStyle("outline", "0px none");
28380     this.getEl().unselectable();
28381     if (this.dragGroup) {
28382                 this.setDraggable(this.dragGroup.split(","));
28383     }
28384     if (this.dropGroup) {
28385                 this.setDroppable(this.dropGroup.split(","));
28386     }
28387     if (this.deletable) {
28388         this.setDeletable();
28389     }
28390     this.isDirtyFlag = false;
28391         this.addEvents({
28392                 "drop" : true
28393         });
28394 };
28395
28396 Roo.extend(Roo.DDView, Roo.View, {
28397 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28398 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28399 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28400 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28401
28402         isFormField: true,
28403
28404         reset: Roo.emptyFn,
28405         
28406         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28407
28408         validate: function() {
28409                 return true;
28410         },
28411         
28412         destroy: function() {
28413                 this.purgeListeners();
28414                 this.getEl.removeAllListeners();
28415                 this.getEl().remove();
28416                 if (this.dragZone) {
28417                         if (this.dragZone.destroy) {
28418                                 this.dragZone.destroy();
28419                         }
28420                 }
28421                 if (this.dropZone) {
28422                         if (this.dropZone.destroy) {
28423                                 this.dropZone.destroy();
28424                         }
28425                 }
28426         },
28427
28428 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28429         getName: function() {
28430                 return this.name;
28431         },
28432
28433 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28434         setValue: function(v) {
28435                 if (!this.store) {
28436                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28437                 }
28438                 var data = {};
28439                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28440                 this.store.proxy = new Roo.data.MemoryProxy(data);
28441                 this.store.load();
28442         },
28443
28444 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28445         getValue: function() {
28446                 var result = '(';
28447                 this.store.each(function(rec) {
28448                         result += rec.id + ',';
28449                 });
28450                 return result.substr(0, result.length - 1) + ')';
28451         },
28452         
28453         getIds: function() {
28454                 var i = 0, result = new Array(this.store.getCount());
28455                 this.store.each(function(rec) {
28456                         result[i++] = rec.id;
28457                 });
28458                 return result;
28459         },
28460         
28461         isDirty: function() {
28462                 return this.isDirtyFlag;
28463         },
28464
28465 /**
28466  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28467  *      whole Element becomes the target, and this causes the drop gesture to append.
28468  */
28469     getTargetFromEvent : function(e) {
28470                 var target = e.getTarget();
28471                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28472                 target = target.parentNode;
28473                 }
28474                 if (!target) {
28475                         target = this.el.dom.lastChild || this.el.dom;
28476                 }
28477                 return target;
28478     },
28479
28480 /**
28481  *      Create the drag data which consists of an object which has the property "ddel" as
28482  *      the drag proxy element. 
28483  */
28484     getDragData : function(e) {
28485         var target = this.findItemFromChild(e.getTarget());
28486                 if(target) {
28487                         this.handleSelection(e);
28488                         var selNodes = this.getSelectedNodes();
28489             var dragData = {
28490                 source: this,
28491                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28492                 nodes: selNodes,
28493                 records: []
28494                         };
28495                         var selectedIndices = this.getSelectedIndexes();
28496                         for (var i = 0; i < selectedIndices.length; i++) {
28497                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28498                         }
28499                         if (selNodes.length == 1) {
28500                                 dragData.ddel = target.cloneNode(true); // the div element
28501                         } else {
28502                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28503                                 div.className = 'multi-proxy';
28504                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28505                                         div.appendChild(selNodes[i].cloneNode(true));
28506                                 }
28507                                 dragData.ddel = div;
28508                         }
28509             //console.log(dragData)
28510             //console.log(dragData.ddel.innerHTML)
28511                         return dragData;
28512                 }
28513         //console.log('nodragData')
28514                 return false;
28515     },
28516     
28517 /**     Specify to which ddGroup items in this DDView may be dragged. */
28518     setDraggable: function(ddGroup) {
28519         if (ddGroup instanceof Array) {
28520                 Roo.each(ddGroup, this.setDraggable, this);
28521                 return;
28522         }
28523         if (this.dragZone) {
28524                 this.dragZone.addToGroup(ddGroup);
28525         } else {
28526                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28527                                 containerScroll: true,
28528                                 ddGroup: ddGroup 
28529
28530                         });
28531 //                      Draggability implies selection. DragZone's mousedown selects the element.
28532                         if (!this.multiSelect) { this.singleSelect = true; }
28533
28534 //                      Wire the DragZone's handlers up to methods in *this*
28535                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28536                 }
28537     },
28538
28539 /**     Specify from which ddGroup this DDView accepts drops. */
28540     setDroppable: function(ddGroup) {
28541         if (ddGroup instanceof Array) {
28542                 Roo.each(ddGroup, this.setDroppable, this);
28543                 return;
28544         }
28545         if (this.dropZone) {
28546                 this.dropZone.addToGroup(ddGroup);
28547         } else {
28548                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28549                                 containerScroll: true,
28550                                 ddGroup: ddGroup
28551                         });
28552
28553 //                      Wire the DropZone's handlers up to methods in *this*
28554                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28555                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28556                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28557                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28558                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28559                 }
28560     },
28561
28562 /**     Decide whether to drop above or below a View node. */
28563     getDropPoint : function(e, n, dd){
28564         if (n == this.el.dom) { return "above"; }
28565                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28566                 var c = t + (b - t) / 2;
28567                 var y = Roo.lib.Event.getPageY(e);
28568                 if(y <= c) {
28569                         return "above";
28570                 }else{
28571                         return "below";
28572                 }
28573     },
28574
28575     onNodeEnter : function(n, dd, e, data){
28576                 return false;
28577     },
28578     
28579     onNodeOver : function(n, dd, e, data){
28580                 var pt = this.getDropPoint(e, n, dd);
28581                 // set the insert point style on the target node
28582                 var dragElClass = this.dropNotAllowed;
28583                 if (pt) {
28584                         var targetElClass;
28585                         if (pt == "above"){
28586                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28587                                 targetElClass = "x-view-drag-insert-above";
28588                         } else {
28589                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28590                                 targetElClass = "x-view-drag-insert-below";
28591                         }
28592                         if (this.lastInsertClass != targetElClass){
28593                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28594                                 this.lastInsertClass = targetElClass;
28595                         }
28596                 }
28597                 return dragElClass;
28598         },
28599
28600     onNodeOut : function(n, dd, e, data){
28601                 this.removeDropIndicators(n);
28602     },
28603
28604     onNodeDrop : function(n, dd, e, data){
28605         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28606                 return false;
28607         }
28608         var pt = this.getDropPoint(e, n, dd);
28609                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28610                 if (pt == "below") { insertAt++; }
28611                 for (var i = 0; i < data.records.length; i++) {
28612                         var r = data.records[i];
28613                         var dup = this.store.getById(r.id);
28614                         if (dup && (dd != this.dragZone)) {
28615                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28616                         } else {
28617                                 if (data.copy) {
28618                                         this.store.insert(insertAt++, r.copy());
28619                                 } else {
28620                                         data.source.isDirtyFlag = true;
28621                                         r.store.remove(r);
28622                                         this.store.insert(insertAt++, r);
28623                                 }
28624                                 this.isDirtyFlag = true;
28625                         }
28626                 }
28627                 this.dragZone.cachedTarget = null;
28628                 return true;
28629     },
28630
28631     removeDropIndicators : function(n){
28632                 if(n){
28633                         Roo.fly(n).removeClass([
28634                                 "x-view-drag-insert-above",
28635                                 "x-view-drag-insert-below"]);
28636                         this.lastInsertClass = "_noclass";
28637                 }
28638     },
28639
28640 /**
28641  *      Utility method. Add a delete option to the DDView's context menu.
28642  *      @param {String} imageUrl The URL of the "delete" icon image.
28643  */
28644         setDeletable: function(imageUrl) {
28645                 if (!this.singleSelect && !this.multiSelect) {
28646                         this.singleSelect = true;
28647                 }
28648                 var c = this.getContextMenu();
28649                 this.contextMenu.on("itemclick", function(item) {
28650                         switch (item.id) {
28651                                 case "delete":
28652                                         this.remove(this.getSelectedIndexes());
28653                                         break;
28654                         }
28655                 }, this);
28656                 this.contextMenu.add({
28657                         icon: imageUrl,
28658                         id: "delete",
28659                         text: 'Delete'
28660                 });
28661         },
28662         
28663 /**     Return the context menu for this DDView. */
28664         getContextMenu: function() {
28665                 if (!this.contextMenu) {
28666 //                      Create the View's context menu
28667                         this.contextMenu = new Roo.menu.Menu({
28668                                 id: this.id + "-contextmenu"
28669                         });
28670                         this.el.on("contextmenu", this.showContextMenu, this);
28671                 }
28672                 return this.contextMenu;
28673         },
28674         
28675         disableContextMenu: function() {
28676                 if (this.contextMenu) {
28677                         this.el.un("contextmenu", this.showContextMenu, this);
28678                 }
28679         },
28680
28681         showContextMenu: function(e, item) {
28682         item = this.findItemFromChild(e.getTarget());
28683                 if (item) {
28684                         e.stopEvent();
28685                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28686                         this.contextMenu.showAt(e.getXY());
28687             }
28688     },
28689
28690 /**
28691  *      Remove {@link Roo.data.Record}s at the specified indices.
28692  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28693  */
28694     remove: function(selectedIndices) {
28695                 selectedIndices = [].concat(selectedIndices);
28696                 for (var i = 0; i < selectedIndices.length; i++) {
28697                         var rec = this.store.getAt(selectedIndices[i]);
28698                         this.store.remove(rec);
28699                 }
28700     },
28701
28702 /**
28703  *      Double click fires the event, but also, if this is draggable, and there is only one other
28704  *      related DropZone, it transfers the selected node.
28705  */
28706     onDblClick : function(e){
28707         var item = this.findItemFromChild(e.getTarget());
28708         if(item){
28709             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28710                 return false;
28711             }
28712             if (this.dragGroup) {
28713                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28714                     while (targets.indexOf(this.dropZone) > -1) {
28715                             targets.remove(this.dropZone);
28716                                 }
28717                     if (targets.length == 1) {
28718                                         this.dragZone.cachedTarget = null;
28719                         var el = Roo.get(targets[0].getEl());
28720                         var box = el.getBox(true);
28721                         targets[0].onNodeDrop(el.dom, {
28722                                 target: el.dom,
28723                                 xy: [box.x, box.y + box.height - 1]
28724                         }, null, this.getDragData(e));
28725                     }
28726                 }
28727         }
28728     },
28729     
28730     handleSelection: function(e) {
28731                 this.dragZone.cachedTarget = null;
28732         var item = this.findItemFromChild(e.getTarget());
28733         if (!item) {
28734                 this.clearSelections(true);
28735                 return;
28736         }
28737                 if (item && (this.multiSelect || this.singleSelect)){
28738                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28739                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28740                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28741                                 this.unselect(item);
28742                         } else {
28743                                 this.select(item, this.multiSelect && e.ctrlKey);
28744                                 this.lastSelection = item;
28745                         }
28746                 }
28747     },
28748
28749     onItemClick : function(item, index, e){
28750                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28751                         return false;
28752                 }
28753                 return true;
28754     },
28755
28756     unselect : function(nodeInfo, suppressEvent){
28757                 var node = this.getNode(nodeInfo);
28758                 if(node && this.isSelected(node)){
28759                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28760                                 Roo.fly(node).removeClass(this.selectedClass);
28761                                 this.selections.remove(node);
28762                                 if(!suppressEvent){
28763                                         this.fireEvent("selectionchange", this, this.selections);
28764                                 }
28765                         }
28766                 }
28767     }
28768 });
28769 /*
28770  * Based on:
28771  * Ext JS Library 1.1.1
28772  * Copyright(c) 2006-2007, Ext JS, LLC.
28773  *
28774  * Originally Released Under LGPL - original licence link has changed is not relivant.
28775  *
28776  * Fork - LGPL
28777  * <script type="text/javascript">
28778  */
28779  
28780 /**
28781  * @class Roo.LayoutManager
28782  * @extends Roo.util.Observable
28783  * Base class for layout managers.
28784  */
28785 Roo.LayoutManager = function(container, config){
28786     Roo.LayoutManager.superclass.constructor.call(this);
28787     this.el = Roo.get(container);
28788     // ie scrollbar fix
28789     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28790         document.body.scroll = "no";
28791     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28792         this.el.position('relative');
28793     }
28794     this.id = this.el.id;
28795     this.el.addClass("x-layout-container");
28796     /** false to disable window resize monitoring @type Boolean */
28797     this.monitorWindowResize = true;
28798     this.regions = {};
28799     this.addEvents({
28800         /**
28801          * @event layout
28802          * Fires when a layout is performed. 
28803          * @param {Roo.LayoutManager} this
28804          */
28805         "layout" : true,
28806         /**
28807          * @event regionresized
28808          * Fires when the user resizes a region. 
28809          * @param {Roo.LayoutRegion} region The resized region
28810          * @param {Number} newSize The new size (width for east/west, height for north/south)
28811          */
28812         "regionresized" : true,
28813         /**
28814          * @event regioncollapsed
28815          * Fires when a region is collapsed. 
28816          * @param {Roo.LayoutRegion} region The collapsed region
28817          */
28818         "regioncollapsed" : true,
28819         /**
28820          * @event regionexpanded
28821          * Fires when a region is expanded.  
28822          * @param {Roo.LayoutRegion} region The expanded region
28823          */
28824         "regionexpanded" : true
28825     });
28826     this.updating = false;
28827     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28828 };
28829
28830 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28831     /**
28832      * Returns true if this layout is currently being updated
28833      * @return {Boolean}
28834      */
28835     isUpdating : function(){
28836         return this.updating; 
28837     },
28838     
28839     /**
28840      * Suspend the LayoutManager from doing auto-layouts while
28841      * making multiple add or remove calls
28842      */
28843     beginUpdate : function(){
28844         this.updating = true;    
28845     },
28846     
28847     /**
28848      * Restore auto-layouts and optionally disable the manager from performing a layout
28849      * @param {Boolean} noLayout true to disable a layout update 
28850      */
28851     endUpdate : function(noLayout){
28852         this.updating = false;
28853         if(!noLayout){
28854             this.layout();
28855         }    
28856     },
28857     
28858     layout: function(){
28859         
28860     },
28861     
28862     onRegionResized : function(region, newSize){
28863         this.fireEvent("regionresized", region, newSize);
28864         this.layout();
28865     },
28866     
28867     onRegionCollapsed : function(region){
28868         this.fireEvent("regioncollapsed", region);
28869     },
28870     
28871     onRegionExpanded : function(region){
28872         this.fireEvent("regionexpanded", region);
28873     },
28874         
28875     /**
28876      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28877      * performs box-model adjustments.
28878      * @return {Object} The size as an object {width: (the width), height: (the height)}
28879      */
28880     getViewSize : function(){
28881         var size;
28882         if(this.el.dom != document.body){
28883             size = this.el.getSize();
28884         }else{
28885             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28886         }
28887         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28888         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28889         return size;
28890     },
28891     
28892     /**
28893      * Returns the Element this layout is bound to.
28894      * @return {Roo.Element}
28895      */
28896     getEl : function(){
28897         return this.el;
28898     },
28899     
28900     /**
28901      * Returns the specified region.
28902      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28903      * @return {Roo.LayoutRegion}
28904      */
28905     getRegion : function(target){
28906         return this.regions[target.toLowerCase()];
28907     },
28908     
28909     onWindowResize : function(){
28910         if(this.monitorWindowResize){
28911             this.layout();
28912         }
28913     }
28914 });/*
28915  * Based on:
28916  * Ext JS Library 1.1.1
28917  * Copyright(c) 2006-2007, Ext JS, LLC.
28918  *
28919  * Originally Released Under LGPL - original licence link has changed is not relivant.
28920  *
28921  * Fork - LGPL
28922  * <script type="text/javascript">
28923  */
28924 /**
28925  * @class Roo.BorderLayout
28926  * @extends Roo.LayoutManager
28927  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28928  * please see: <br><br>
28929  * <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>
28930  * <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>
28931  * Example:
28932  <pre><code>
28933  var layout = new Roo.BorderLayout(document.body, {
28934     north: {
28935         initialSize: 25,
28936         titlebar: false
28937     },
28938     west: {
28939         split:true,
28940         initialSize: 200,
28941         minSize: 175,
28942         maxSize: 400,
28943         titlebar: true,
28944         collapsible: true
28945     },
28946     east: {
28947         split:true,
28948         initialSize: 202,
28949         minSize: 175,
28950         maxSize: 400,
28951         titlebar: true,
28952         collapsible: true
28953     },
28954     south: {
28955         split:true,
28956         initialSize: 100,
28957         minSize: 100,
28958         maxSize: 200,
28959         titlebar: true,
28960         collapsible: true
28961     },
28962     center: {
28963         titlebar: true,
28964         autoScroll:true,
28965         resizeTabs: true,
28966         minTabWidth: 50,
28967         preferredTabWidth: 150
28968     }
28969 });
28970
28971 // shorthand
28972 var CP = Roo.ContentPanel;
28973
28974 layout.beginUpdate();
28975 layout.add("north", new CP("north", "North"));
28976 layout.add("south", new CP("south", {title: "South", closable: true}));
28977 layout.add("west", new CP("west", {title: "West"}));
28978 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28979 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28980 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28981 layout.getRegion("center").showPanel("center1");
28982 layout.endUpdate();
28983 </code></pre>
28984
28985 <b>The container the layout is rendered into can be either the body element or any other element.
28986 If it is not the body element, the container needs to either be an absolute positioned element,
28987 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28988 the container size if it is not the body element.</b>
28989
28990 * @constructor
28991 * Create a new BorderLayout
28992 * @param {String/HTMLElement/Element} container The container this layout is bound to
28993 * @param {Object} config Configuration options
28994  */
28995 Roo.BorderLayout = function(container, config){
28996     config = config || {};
28997     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28998     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28999     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29000         var target = this.factory.validRegions[i];
29001         if(config[target]){
29002             this.addRegion(target, config[target]);
29003         }
29004     }
29005 };
29006
29007 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29008     /**
29009      * Creates and adds a new region if it doesn't already exist.
29010      * @param {String} target The target region key (north, south, east, west or center).
29011      * @param {Object} config The regions config object
29012      * @return {BorderLayoutRegion} The new region
29013      */
29014     addRegion : function(target, config){
29015         if(!this.regions[target]){
29016             var r = this.factory.create(target, this, config);
29017             this.bindRegion(target, r);
29018         }
29019         return this.regions[target];
29020     },
29021
29022     // private (kinda)
29023     bindRegion : function(name, r){
29024         this.regions[name] = r;
29025         r.on("visibilitychange", this.layout, this);
29026         r.on("paneladded", this.layout, this);
29027         r.on("panelremoved", this.layout, this);
29028         r.on("invalidated", this.layout, this);
29029         r.on("resized", this.onRegionResized, this);
29030         r.on("collapsed", this.onRegionCollapsed, this);
29031         r.on("expanded", this.onRegionExpanded, this);
29032     },
29033
29034     /**
29035      * Performs a layout update.
29036      */
29037     layout : function(){
29038         if(this.updating) {
29039             return;
29040         }
29041         var size = this.getViewSize();
29042         var w = size.width;
29043         var h = size.height;
29044         var centerW = w;
29045         var centerH = h;
29046         var centerY = 0;
29047         var centerX = 0;
29048         //var x = 0, y = 0;
29049
29050         var rs = this.regions;
29051         var north = rs["north"];
29052         var south = rs["south"]; 
29053         var west = rs["west"];
29054         var east = rs["east"];
29055         var center = rs["center"];
29056         //if(this.hideOnLayout){ // not supported anymore
29057             //c.el.setStyle("display", "none");
29058         //}
29059         if(north && north.isVisible()){
29060             var b = north.getBox();
29061             var m = north.getMargins();
29062             b.width = w - (m.left+m.right);
29063             b.x = m.left;
29064             b.y = m.top;
29065             centerY = b.height + b.y + m.bottom;
29066             centerH -= centerY;
29067             north.updateBox(this.safeBox(b));
29068         }
29069         if(south && south.isVisible()){
29070             var b = south.getBox();
29071             var m = south.getMargins();
29072             b.width = w - (m.left+m.right);
29073             b.x = m.left;
29074             var totalHeight = (b.height + m.top + m.bottom);
29075             b.y = h - totalHeight + m.top;
29076             centerH -= totalHeight;
29077             south.updateBox(this.safeBox(b));
29078         }
29079         if(west && west.isVisible()){
29080             var b = west.getBox();
29081             var m = west.getMargins();
29082             b.height = centerH - (m.top+m.bottom);
29083             b.x = m.left;
29084             b.y = centerY + m.top;
29085             var totalWidth = (b.width + m.left + m.right);
29086             centerX += totalWidth;
29087             centerW -= totalWidth;
29088             west.updateBox(this.safeBox(b));
29089         }
29090         if(east && east.isVisible()){
29091             var b = east.getBox();
29092             var m = east.getMargins();
29093             b.height = centerH - (m.top+m.bottom);
29094             var totalWidth = (b.width + m.left + m.right);
29095             b.x = w - totalWidth + m.left;
29096             b.y = centerY + m.top;
29097             centerW -= totalWidth;
29098             east.updateBox(this.safeBox(b));
29099         }
29100         if(center){
29101             var m = center.getMargins();
29102             var centerBox = {
29103                 x: centerX + m.left,
29104                 y: centerY + m.top,
29105                 width: centerW - (m.left+m.right),
29106                 height: centerH - (m.top+m.bottom)
29107             };
29108             //if(this.hideOnLayout){
29109                 //center.el.setStyle("display", "block");
29110             //}
29111             center.updateBox(this.safeBox(centerBox));
29112         }
29113         this.el.repaint();
29114         this.fireEvent("layout", this);
29115     },
29116
29117     // private
29118     safeBox : function(box){
29119         box.width = Math.max(0, box.width);
29120         box.height = Math.max(0, box.height);
29121         return box;
29122     },
29123
29124     /**
29125      * Adds a ContentPanel (or subclass) to this layout.
29126      * @param {String} target The target region key (north, south, east, west or center).
29127      * @param {Roo.ContentPanel} panel The panel to add
29128      * @return {Roo.ContentPanel} The added panel
29129      */
29130     add : function(target, panel){
29131          
29132         target = target.toLowerCase();
29133         return this.regions[target].add(panel);
29134     },
29135
29136     /**
29137      * Remove a ContentPanel (or subclass) to this layout.
29138      * @param {String} target The target region key (north, south, east, west or center).
29139      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29140      * @return {Roo.ContentPanel} The removed panel
29141      */
29142     remove : function(target, panel){
29143         target = target.toLowerCase();
29144         return this.regions[target].remove(panel);
29145     },
29146
29147     /**
29148      * Searches all regions for a panel with the specified id
29149      * @param {String} panelId
29150      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29151      */
29152     findPanel : function(panelId){
29153         var rs = this.regions;
29154         for(var target in rs){
29155             if(typeof rs[target] != "function"){
29156                 var p = rs[target].getPanel(panelId);
29157                 if(p){
29158                     return p;
29159                 }
29160             }
29161         }
29162         return null;
29163     },
29164
29165     /**
29166      * Searches all regions for a panel with the specified id and activates (shows) it.
29167      * @param {String/ContentPanel} panelId The panels id or the panel itself
29168      * @return {Roo.ContentPanel} The shown panel or null
29169      */
29170     showPanel : function(panelId) {
29171       var rs = this.regions;
29172       for(var target in rs){
29173          var r = rs[target];
29174          if(typeof r != "function"){
29175             if(r.hasPanel(panelId)){
29176                return r.showPanel(panelId);
29177             }
29178          }
29179       }
29180       return null;
29181    },
29182
29183    /**
29184      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29185      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29186      */
29187     restoreState : function(provider){
29188         if(!provider){
29189             provider = Roo.state.Manager;
29190         }
29191         var sm = new Roo.LayoutStateManager();
29192         sm.init(this, provider);
29193     },
29194
29195     /**
29196      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29197      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29198      * a valid ContentPanel config object.  Example:
29199      * <pre><code>
29200 // Create the main layout
29201 var layout = new Roo.BorderLayout('main-ct', {
29202     west: {
29203         split:true,
29204         minSize: 175,
29205         titlebar: true
29206     },
29207     center: {
29208         title:'Components'
29209     }
29210 }, 'main-ct');
29211
29212 // Create and add multiple ContentPanels at once via configs
29213 layout.batchAdd({
29214    west: {
29215        id: 'source-files',
29216        autoCreate:true,
29217        title:'Ext Source Files',
29218        autoScroll:true,
29219        fitToFrame:true
29220    },
29221    center : {
29222        el: cview,
29223        autoScroll:true,
29224        fitToFrame:true,
29225        toolbar: tb,
29226        resizeEl:'cbody'
29227    }
29228 });
29229 </code></pre>
29230      * @param {Object} regions An object containing ContentPanel configs by region name
29231      */
29232     batchAdd : function(regions){
29233         this.beginUpdate();
29234         for(var rname in regions){
29235             var lr = this.regions[rname];
29236             if(lr){
29237                 this.addTypedPanels(lr, regions[rname]);
29238             }
29239         }
29240         this.endUpdate();
29241     },
29242
29243     // private
29244     addTypedPanels : function(lr, ps){
29245         if(typeof ps == 'string'){
29246             lr.add(new Roo.ContentPanel(ps));
29247         }
29248         else if(ps instanceof Array){
29249             for(var i =0, len = ps.length; i < len; i++){
29250                 this.addTypedPanels(lr, ps[i]);
29251             }
29252         }
29253         else if(!ps.events){ // raw config?
29254             var el = ps.el;
29255             delete ps.el; // prevent conflict
29256             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29257         }
29258         else {  // panel object assumed!
29259             lr.add(ps);
29260         }
29261     },
29262     /**
29263      * Adds a xtype elements to the layout.
29264      * <pre><code>
29265
29266 layout.addxtype({
29267        xtype : 'ContentPanel',
29268        region: 'west',
29269        items: [ .... ]
29270    }
29271 );
29272
29273 layout.addxtype({
29274         xtype : 'NestedLayoutPanel',
29275         region: 'west',
29276         layout: {
29277            center: { },
29278            west: { }   
29279         },
29280         items : [ ... list of content panels or nested layout panels.. ]
29281    }
29282 );
29283 </code></pre>
29284      * @param {Object} cfg Xtype definition of item to add.
29285      */
29286     addxtype : function(cfg)
29287     {
29288         // basically accepts a pannel...
29289         // can accept a layout region..!?!?
29290         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29291         
29292         if (!cfg.xtype.match(/Panel$/)) {
29293             return false;
29294         }
29295         var ret = false;
29296         
29297         if (typeof(cfg.region) == 'undefined') {
29298             Roo.log("Failed to add Panel, region was not set");
29299             Roo.log(cfg);
29300             return false;
29301         }
29302         var region = cfg.region;
29303         delete cfg.region;
29304         
29305           
29306         var xitems = [];
29307         if (cfg.items) {
29308             xitems = cfg.items;
29309             delete cfg.items;
29310         }
29311         var nb = false;
29312         
29313         switch(cfg.xtype) 
29314         {
29315             case 'ContentPanel':  // ContentPanel (el, cfg)
29316             case 'ScrollPanel':  // ContentPanel (el, cfg)
29317             case 'ViewPanel': 
29318                 if(cfg.autoCreate) {
29319                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29320                 } else {
29321                     var el = this.el.createChild();
29322                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29323                 }
29324                 
29325                 this.add(region, ret);
29326                 break;
29327             
29328             
29329             case 'TreePanel': // our new panel!
29330                 cfg.el = this.el.createChild();
29331                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29332                 this.add(region, ret);
29333                 break;
29334             
29335             case 'NestedLayoutPanel': 
29336                 // create a new Layout (which is  a Border Layout...
29337                 var el = this.el.createChild();
29338                 var clayout = cfg.layout;
29339                 delete cfg.layout;
29340                 clayout.items   = clayout.items  || [];
29341                 // replace this exitems with the clayout ones..
29342                 xitems = clayout.items;
29343                  
29344                 
29345                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29346                     cfg.background = false;
29347                 }
29348                 var layout = new Roo.BorderLayout(el, clayout);
29349                 
29350                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29351                 //console.log('adding nested layout panel '  + cfg.toSource());
29352                 this.add(region, ret);
29353                 nb = {}; /// find first...
29354                 break;
29355                 
29356             case 'GridPanel': 
29357             
29358                 // needs grid and region
29359                 
29360                 //var el = this.getRegion(region).el.createChild();
29361                 var el = this.el.createChild();
29362                 // create the grid first...
29363                 
29364                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29365                 delete cfg.grid;
29366                 if (region == 'center' && this.active ) {
29367                     cfg.background = false;
29368                 }
29369                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29370                 
29371                 this.add(region, ret);
29372                 if (cfg.background) {
29373                     ret.on('activate', function(gp) {
29374                         if (!gp.grid.rendered) {
29375                             gp.grid.render();
29376                         }
29377                     });
29378                 } else {
29379                     grid.render();
29380                 }
29381                 break;
29382            
29383            
29384            
29385                 
29386                 
29387                 
29388             default:
29389                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29390                     
29391                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29392                     this.add(region, ret);
29393                 } else {
29394                 
29395                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29396                     return null;
29397                 }
29398                 
29399              // GridPanel (grid, cfg)
29400             
29401         }
29402         this.beginUpdate();
29403         // add children..
29404         var region = '';
29405         var abn = {};
29406         Roo.each(xitems, function(i)  {
29407             region = nb && i.region ? i.region : false;
29408             
29409             var add = ret.addxtype(i);
29410            
29411             if (region) {
29412                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29413                 if (!i.background) {
29414                     abn[region] = nb[region] ;
29415                 }
29416             }
29417             
29418         });
29419         this.endUpdate();
29420
29421         // make the last non-background panel active..
29422         //if (nb) { Roo.log(abn); }
29423         if (nb) {
29424             
29425             for(var r in abn) {
29426                 region = this.getRegion(r);
29427                 if (region) {
29428                     // tried using nb[r], but it does not work..
29429                      
29430                     region.showPanel(abn[r]);
29431                    
29432                 }
29433             }
29434         }
29435         return ret;
29436         
29437     }
29438 });
29439
29440 /**
29441  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29442  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29443  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29444  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29445  * <pre><code>
29446 // shorthand
29447 var CP = Roo.ContentPanel;
29448
29449 var layout = Roo.BorderLayout.create({
29450     north: {
29451         initialSize: 25,
29452         titlebar: false,
29453         panels: [new CP("north", "North")]
29454     },
29455     west: {
29456         split:true,
29457         initialSize: 200,
29458         minSize: 175,
29459         maxSize: 400,
29460         titlebar: true,
29461         collapsible: true,
29462         panels: [new CP("west", {title: "West"})]
29463     },
29464     east: {
29465         split:true,
29466         initialSize: 202,
29467         minSize: 175,
29468         maxSize: 400,
29469         titlebar: true,
29470         collapsible: true,
29471         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29472     },
29473     south: {
29474         split:true,
29475         initialSize: 100,
29476         minSize: 100,
29477         maxSize: 200,
29478         titlebar: true,
29479         collapsible: true,
29480         panels: [new CP("south", {title: "South", closable: true})]
29481     },
29482     center: {
29483         titlebar: true,
29484         autoScroll:true,
29485         resizeTabs: true,
29486         minTabWidth: 50,
29487         preferredTabWidth: 150,
29488         panels: [
29489             new CP("center1", {title: "Close Me", closable: true}),
29490             new CP("center2", {title: "Center Panel", closable: false})
29491         ]
29492     }
29493 }, document.body);
29494
29495 layout.getRegion("center").showPanel("center1");
29496 </code></pre>
29497  * @param config
29498  * @param targetEl
29499  */
29500 Roo.BorderLayout.create = function(config, targetEl){
29501     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29502     layout.beginUpdate();
29503     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29504     for(var j = 0, jlen = regions.length; j < jlen; j++){
29505         var lr = regions[j];
29506         if(layout.regions[lr] && config[lr].panels){
29507             var r = layout.regions[lr];
29508             var ps = config[lr].panels;
29509             layout.addTypedPanels(r, ps);
29510         }
29511     }
29512     layout.endUpdate();
29513     return layout;
29514 };
29515
29516 // private
29517 Roo.BorderLayout.RegionFactory = {
29518     // private
29519     validRegions : ["north","south","east","west","center"],
29520
29521     // private
29522     create : function(target, mgr, config){
29523         target = target.toLowerCase();
29524         if(config.lightweight || config.basic){
29525             return new Roo.BasicLayoutRegion(mgr, config, target);
29526         }
29527         switch(target){
29528             case "north":
29529                 return new Roo.NorthLayoutRegion(mgr, config);
29530             case "south":
29531                 return new Roo.SouthLayoutRegion(mgr, config);
29532             case "east":
29533                 return new Roo.EastLayoutRegion(mgr, config);
29534             case "west":
29535                 return new Roo.WestLayoutRegion(mgr, config);
29536             case "center":
29537                 return new Roo.CenterLayoutRegion(mgr, config);
29538         }
29539         throw 'Layout region "'+target+'" not supported.';
29540     }
29541 };/*
29542  * Based on:
29543  * Ext JS Library 1.1.1
29544  * Copyright(c) 2006-2007, Ext JS, LLC.
29545  *
29546  * Originally Released Under LGPL - original licence link has changed is not relivant.
29547  *
29548  * Fork - LGPL
29549  * <script type="text/javascript">
29550  */
29551  
29552 /**
29553  * @class Roo.BasicLayoutRegion
29554  * @extends Roo.util.Observable
29555  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29556  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29557  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29558  */
29559 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29560     this.mgr = mgr;
29561     this.position  = pos;
29562     this.events = {
29563         /**
29564          * @scope Roo.BasicLayoutRegion
29565          */
29566         
29567         /**
29568          * @event beforeremove
29569          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29570          * @param {Roo.LayoutRegion} this
29571          * @param {Roo.ContentPanel} panel The panel
29572          * @param {Object} e The cancel event object
29573          */
29574         "beforeremove" : true,
29575         /**
29576          * @event invalidated
29577          * Fires when the layout for this region is changed.
29578          * @param {Roo.LayoutRegion} this
29579          */
29580         "invalidated" : true,
29581         /**
29582          * @event visibilitychange
29583          * Fires when this region is shown or hidden 
29584          * @param {Roo.LayoutRegion} this
29585          * @param {Boolean} visibility true or false
29586          */
29587         "visibilitychange" : true,
29588         /**
29589          * @event paneladded
29590          * Fires when a panel is added. 
29591          * @param {Roo.LayoutRegion} this
29592          * @param {Roo.ContentPanel} panel The panel
29593          */
29594         "paneladded" : true,
29595         /**
29596          * @event panelremoved
29597          * Fires when a panel is removed. 
29598          * @param {Roo.LayoutRegion} this
29599          * @param {Roo.ContentPanel} panel The panel
29600          */
29601         "panelremoved" : true,
29602         /**
29603          * @event beforecollapse
29604          * Fires when this region before collapse.
29605          * @param {Roo.LayoutRegion} this
29606          */
29607         "beforecollapse" : true,
29608         /**
29609          * @event collapsed
29610          * Fires when this region is collapsed.
29611          * @param {Roo.LayoutRegion} this
29612          */
29613         "collapsed" : true,
29614         /**
29615          * @event expanded
29616          * Fires when this region is expanded.
29617          * @param {Roo.LayoutRegion} this
29618          */
29619         "expanded" : true,
29620         /**
29621          * @event slideshow
29622          * Fires when this region is slid into view.
29623          * @param {Roo.LayoutRegion} this
29624          */
29625         "slideshow" : true,
29626         /**
29627          * @event slidehide
29628          * Fires when this region slides out of view. 
29629          * @param {Roo.LayoutRegion} this
29630          */
29631         "slidehide" : true,
29632         /**
29633          * @event panelactivated
29634          * Fires when a panel is activated. 
29635          * @param {Roo.LayoutRegion} this
29636          * @param {Roo.ContentPanel} panel The activated panel
29637          */
29638         "panelactivated" : true,
29639         /**
29640          * @event resized
29641          * Fires when the user resizes this region. 
29642          * @param {Roo.LayoutRegion} this
29643          * @param {Number} newSize The new size (width for east/west, height for north/south)
29644          */
29645         "resized" : true
29646     };
29647     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29648     this.panels = new Roo.util.MixedCollection();
29649     this.panels.getKey = this.getPanelId.createDelegate(this);
29650     this.box = null;
29651     this.activePanel = null;
29652     // ensure listeners are added...
29653     
29654     if (config.listeners || config.events) {
29655         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29656             listeners : config.listeners || {},
29657             events : config.events || {}
29658         });
29659     }
29660     
29661     if(skipConfig !== true){
29662         this.applyConfig(config);
29663     }
29664 };
29665
29666 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29667     getPanelId : function(p){
29668         return p.getId();
29669     },
29670     
29671     applyConfig : function(config){
29672         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29673         this.config = config;
29674         
29675     },
29676     
29677     /**
29678      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29679      * the width, for horizontal (north, south) the height.
29680      * @param {Number} newSize The new width or height
29681      */
29682     resizeTo : function(newSize){
29683         var el = this.el ? this.el :
29684                  (this.activePanel ? this.activePanel.getEl() : null);
29685         if(el){
29686             switch(this.position){
29687                 case "east":
29688                 case "west":
29689                     el.setWidth(newSize);
29690                     this.fireEvent("resized", this, newSize);
29691                 break;
29692                 case "north":
29693                 case "south":
29694                     el.setHeight(newSize);
29695                     this.fireEvent("resized", this, newSize);
29696                 break;                
29697             }
29698         }
29699     },
29700     
29701     getBox : function(){
29702         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29703     },
29704     
29705     getMargins : function(){
29706         return this.margins;
29707     },
29708     
29709     updateBox : function(box){
29710         this.box = box;
29711         var el = this.activePanel.getEl();
29712         el.dom.style.left = box.x + "px";
29713         el.dom.style.top = box.y + "px";
29714         this.activePanel.setSize(box.width, box.height);
29715     },
29716     
29717     /**
29718      * Returns the container element for this region.
29719      * @return {Roo.Element}
29720      */
29721     getEl : function(){
29722         return this.activePanel;
29723     },
29724     
29725     /**
29726      * Returns true if this region is currently visible.
29727      * @return {Boolean}
29728      */
29729     isVisible : function(){
29730         return this.activePanel ? true : false;
29731     },
29732     
29733     setActivePanel : function(panel){
29734         panel = this.getPanel(panel);
29735         if(this.activePanel && this.activePanel != panel){
29736             this.activePanel.setActiveState(false);
29737             this.activePanel.getEl().setLeftTop(-10000,-10000);
29738         }
29739         this.activePanel = panel;
29740         panel.setActiveState(true);
29741         if(this.box){
29742             panel.setSize(this.box.width, this.box.height);
29743         }
29744         this.fireEvent("panelactivated", this, panel);
29745         this.fireEvent("invalidated");
29746     },
29747     
29748     /**
29749      * Show the specified panel.
29750      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29751      * @return {Roo.ContentPanel} The shown panel or null
29752      */
29753     showPanel : function(panel){
29754         if(panel = this.getPanel(panel)){
29755             this.setActivePanel(panel);
29756         }
29757         return panel;
29758     },
29759     
29760     /**
29761      * Get the active panel for this region.
29762      * @return {Roo.ContentPanel} The active panel or null
29763      */
29764     getActivePanel : function(){
29765         return this.activePanel;
29766     },
29767     
29768     /**
29769      * Add the passed ContentPanel(s)
29770      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29771      * @return {Roo.ContentPanel} The panel added (if only one was added)
29772      */
29773     add : function(panel){
29774         if(arguments.length > 1){
29775             for(var i = 0, len = arguments.length; i < len; i++) {
29776                 this.add(arguments[i]);
29777             }
29778             return null;
29779         }
29780         if(this.hasPanel(panel)){
29781             this.showPanel(panel);
29782             return panel;
29783         }
29784         var el = panel.getEl();
29785         if(el.dom.parentNode != this.mgr.el.dom){
29786             this.mgr.el.dom.appendChild(el.dom);
29787         }
29788         if(panel.setRegion){
29789             panel.setRegion(this);
29790         }
29791         this.panels.add(panel);
29792         el.setStyle("position", "absolute");
29793         if(!panel.background){
29794             this.setActivePanel(panel);
29795             if(this.config.initialSize && this.panels.getCount()==1){
29796                 this.resizeTo(this.config.initialSize);
29797             }
29798         }
29799         this.fireEvent("paneladded", this, panel);
29800         return panel;
29801     },
29802     
29803     /**
29804      * Returns true if the panel is in this region.
29805      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29806      * @return {Boolean}
29807      */
29808     hasPanel : function(panel){
29809         if(typeof panel == "object"){ // must be panel obj
29810             panel = panel.getId();
29811         }
29812         return this.getPanel(panel) ? true : false;
29813     },
29814     
29815     /**
29816      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29817      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29818      * @param {Boolean} preservePanel Overrides the config preservePanel option
29819      * @return {Roo.ContentPanel} The panel that was removed
29820      */
29821     remove : function(panel, preservePanel){
29822         panel = this.getPanel(panel);
29823         if(!panel){
29824             return null;
29825         }
29826         var e = {};
29827         this.fireEvent("beforeremove", this, panel, e);
29828         if(e.cancel === true){
29829             return null;
29830         }
29831         var panelId = panel.getId();
29832         this.panels.removeKey(panelId);
29833         return panel;
29834     },
29835     
29836     /**
29837      * Returns the panel specified or null if it's not in this region.
29838      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29839      * @return {Roo.ContentPanel}
29840      */
29841     getPanel : function(id){
29842         if(typeof id == "object"){ // must be panel obj
29843             return id;
29844         }
29845         return this.panels.get(id);
29846     },
29847     
29848     /**
29849      * Returns this regions position (north/south/east/west/center).
29850      * @return {String} 
29851      */
29852     getPosition: function(){
29853         return this.position;    
29854     }
29855 });/*
29856  * Based on:
29857  * Ext JS Library 1.1.1
29858  * Copyright(c) 2006-2007, Ext JS, LLC.
29859  *
29860  * Originally Released Under LGPL - original licence link has changed is not relivant.
29861  *
29862  * Fork - LGPL
29863  * <script type="text/javascript">
29864  */
29865  
29866 /**
29867  * @class Roo.LayoutRegion
29868  * @extends Roo.BasicLayoutRegion
29869  * This class represents a region in a layout manager.
29870  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29871  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29872  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29873  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29874  * @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})
29875  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
29876  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29877  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29878  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29879  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29880  * @cfg {String}    title           The title for the region (overrides panel titles)
29881  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29882  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29883  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29884  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29885  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29886  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29887  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29888  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29889  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29890  * @cfg {Boolean}   showPin         True to show a pin button
29891  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29892  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29893  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29894  * @cfg {Number}    width           For East/West panels
29895  * @cfg {Number}    height          For North/South panels
29896  * @cfg {Boolean}   split           To show the splitter
29897  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29898  */
29899 Roo.LayoutRegion = function(mgr, config, pos){
29900     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29901     var dh = Roo.DomHelper;
29902     /** This region's container element 
29903     * @type Roo.Element */
29904     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29905     /** This region's title element 
29906     * @type Roo.Element */
29907
29908     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29909         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29910         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29911     ]}, true);
29912     this.titleEl.enableDisplayMode();
29913     /** This region's title text element 
29914     * @type HTMLElement */
29915     this.titleTextEl = this.titleEl.dom.firstChild;
29916     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29917     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29918     this.closeBtn.enableDisplayMode();
29919     this.closeBtn.on("click", this.closeClicked, this);
29920     this.closeBtn.hide();
29921
29922     this.createBody(config);
29923     this.visible = true;
29924     this.collapsed = false;
29925
29926     if(config.hideWhenEmpty){
29927         this.hide();
29928         this.on("paneladded", this.validateVisibility, this);
29929         this.on("panelremoved", this.validateVisibility, this);
29930     }
29931     this.applyConfig(config);
29932 };
29933
29934 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29935
29936     createBody : function(){
29937         /** This region's body element 
29938         * @type Roo.Element */
29939         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29940     },
29941
29942     applyConfig : function(c){
29943         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29944             var dh = Roo.DomHelper;
29945             if(c.titlebar !== false){
29946                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29947                 this.collapseBtn.on("click", this.collapse, this);
29948                 this.collapseBtn.enableDisplayMode();
29949
29950                 if(c.showPin === true || this.showPin){
29951                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29952                     this.stickBtn.enableDisplayMode();
29953                     this.stickBtn.on("click", this.expand, this);
29954                     this.stickBtn.hide();
29955                 }
29956             }
29957             /** This region's collapsed element
29958             * @type Roo.Element */
29959             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29960                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29961             ]}, true);
29962             if(c.floatable !== false){
29963                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29964                this.collapsedEl.on("click", this.collapseClick, this);
29965             }
29966
29967             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29968                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29969                    id: "message", unselectable: "on", style:{"float":"left"}});
29970                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29971              }
29972             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29973             this.expandBtn.on("click", this.expand, this);
29974         }
29975         if(this.collapseBtn){
29976             this.collapseBtn.setVisible(c.collapsible == true);
29977         }
29978         this.cmargins = c.cmargins || this.cmargins ||
29979                          (this.position == "west" || this.position == "east" ?
29980                              {top: 0, left: 2, right:2, bottom: 0} :
29981                              {top: 2, left: 0, right:0, bottom: 2});
29982         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29983         this.bottomTabs = c.tabPosition != "top";
29984         this.autoScroll = c.autoScroll || false;
29985         if(this.autoScroll){
29986             this.bodyEl.setStyle("overflow", "auto");
29987         }else{
29988             this.bodyEl.setStyle("overflow", "hidden");
29989         }
29990         //if(c.titlebar !== false){
29991             if((!c.titlebar && !c.title) || c.titlebar === false){
29992                 this.titleEl.hide();
29993             }else{
29994                 this.titleEl.show();
29995                 if(c.title){
29996                     this.titleTextEl.innerHTML = c.title;
29997                 }
29998             }
29999         //}
30000         this.duration = c.duration || .30;
30001         this.slideDuration = c.slideDuration || .45;
30002         this.config = c;
30003         if(c.collapsed){
30004             this.collapse(true);
30005         }
30006         if(c.hidden){
30007             this.hide();
30008         }
30009     },
30010     /**
30011      * Returns true if this region is currently visible.
30012      * @return {Boolean}
30013      */
30014     isVisible : function(){
30015         return this.visible;
30016     },
30017
30018     /**
30019      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30020      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30021      */
30022     setCollapsedTitle : function(title){
30023         title = title || "&#160;";
30024         if(this.collapsedTitleTextEl){
30025             this.collapsedTitleTextEl.innerHTML = title;
30026         }
30027     },
30028
30029     getBox : function(){
30030         var b;
30031         if(!this.collapsed){
30032             b = this.el.getBox(false, true);
30033         }else{
30034             b = this.collapsedEl.getBox(false, true);
30035         }
30036         return b;
30037     },
30038
30039     getMargins : function(){
30040         return this.collapsed ? this.cmargins : this.margins;
30041     },
30042
30043     highlight : function(){
30044         this.el.addClass("x-layout-panel-dragover");
30045     },
30046
30047     unhighlight : function(){
30048         this.el.removeClass("x-layout-panel-dragover");
30049     },
30050
30051     updateBox : function(box){
30052         this.box = box;
30053         if(!this.collapsed){
30054             this.el.dom.style.left = box.x + "px";
30055             this.el.dom.style.top = box.y + "px";
30056             this.updateBody(box.width, box.height);
30057         }else{
30058             this.collapsedEl.dom.style.left = box.x + "px";
30059             this.collapsedEl.dom.style.top = box.y + "px";
30060             this.collapsedEl.setSize(box.width, box.height);
30061         }
30062         if(this.tabs){
30063             this.tabs.autoSizeTabs();
30064         }
30065     },
30066
30067     updateBody : function(w, h){
30068         if(w !== null){
30069             this.el.setWidth(w);
30070             w -= this.el.getBorderWidth("rl");
30071             if(this.config.adjustments){
30072                 w += this.config.adjustments[0];
30073             }
30074         }
30075         if(h !== null){
30076             this.el.setHeight(h);
30077             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30078             h -= this.el.getBorderWidth("tb");
30079             if(this.config.adjustments){
30080                 h += this.config.adjustments[1];
30081             }
30082             this.bodyEl.setHeight(h);
30083             if(this.tabs){
30084                 h = this.tabs.syncHeight(h);
30085             }
30086         }
30087         if(this.panelSize){
30088             w = w !== null ? w : this.panelSize.width;
30089             h = h !== null ? h : this.panelSize.height;
30090         }
30091         if(this.activePanel){
30092             var el = this.activePanel.getEl();
30093             w = w !== null ? w : el.getWidth();
30094             h = h !== null ? h : el.getHeight();
30095             this.panelSize = {width: w, height: h};
30096             this.activePanel.setSize(w, h);
30097         }
30098         if(Roo.isIE && this.tabs){
30099             this.tabs.el.repaint();
30100         }
30101     },
30102
30103     /**
30104      * Returns the container element for this region.
30105      * @return {Roo.Element}
30106      */
30107     getEl : function(){
30108         return this.el;
30109     },
30110
30111     /**
30112      * Hides this region.
30113      */
30114     hide : function(){
30115         if(!this.collapsed){
30116             this.el.dom.style.left = "-2000px";
30117             this.el.hide();
30118         }else{
30119             this.collapsedEl.dom.style.left = "-2000px";
30120             this.collapsedEl.hide();
30121         }
30122         this.visible = false;
30123         this.fireEvent("visibilitychange", this, false);
30124     },
30125
30126     /**
30127      * Shows this region if it was previously hidden.
30128      */
30129     show : function(){
30130         if(!this.collapsed){
30131             this.el.show();
30132         }else{
30133             this.collapsedEl.show();
30134         }
30135         this.visible = true;
30136         this.fireEvent("visibilitychange", this, true);
30137     },
30138
30139     closeClicked : function(){
30140         if(this.activePanel){
30141             this.remove(this.activePanel);
30142         }
30143     },
30144
30145     collapseClick : function(e){
30146         if(this.isSlid){
30147            e.stopPropagation();
30148            this.slideIn();
30149         }else{
30150            e.stopPropagation();
30151            this.slideOut();
30152         }
30153     },
30154
30155     /**
30156      * Collapses this region.
30157      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30158      */
30159     collapse : function(skipAnim, skipCheck = false){
30160         if(this.collapsed) {
30161             return;
30162         }
30163         
30164         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30165             
30166             this.collapsed = true;
30167             if(this.split){
30168                 this.split.el.hide();
30169             }
30170             if(this.config.animate && skipAnim !== true){
30171                 this.fireEvent("invalidated", this);
30172                 this.animateCollapse();
30173             }else{
30174                 this.el.setLocation(-20000,-20000);
30175                 this.el.hide();
30176                 this.collapsedEl.show();
30177                 this.fireEvent("collapsed", this);
30178                 this.fireEvent("invalidated", this);
30179             }
30180         }
30181         
30182     },
30183
30184     animateCollapse : function(){
30185         // overridden
30186     },
30187
30188     /**
30189      * Expands this region if it was previously collapsed.
30190      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30191      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30192      */
30193     expand : function(e, skipAnim){
30194         if(e) {
30195             e.stopPropagation();
30196         }
30197         if(!this.collapsed || this.el.hasActiveFx()) {
30198             return;
30199         }
30200         if(this.isSlid){
30201             this.afterSlideIn();
30202             skipAnim = true;
30203         }
30204         this.collapsed = false;
30205         if(this.config.animate && skipAnim !== true){
30206             this.animateExpand();
30207         }else{
30208             this.el.show();
30209             if(this.split){
30210                 this.split.el.show();
30211             }
30212             this.collapsedEl.setLocation(-2000,-2000);
30213             this.collapsedEl.hide();
30214             this.fireEvent("invalidated", this);
30215             this.fireEvent("expanded", this);
30216         }
30217     },
30218
30219     animateExpand : function(){
30220         // overridden
30221     },
30222
30223     initTabs : function()
30224     {
30225         this.bodyEl.setStyle("overflow", "hidden");
30226         var ts = new Roo.TabPanel(
30227                 this.bodyEl.dom,
30228                 {
30229                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30230                     disableTooltips: this.config.disableTabTips,
30231                     toolbar : this.config.toolbar
30232                 }
30233         );
30234         if(this.config.hideTabs){
30235             ts.stripWrap.setDisplayed(false);
30236         }
30237         this.tabs = ts;
30238         ts.resizeTabs = this.config.resizeTabs === true;
30239         ts.minTabWidth = this.config.minTabWidth || 40;
30240         ts.maxTabWidth = this.config.maxTabWidth || 250;
30241         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30242         ts.monitorResize = false;
30243         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30244         ts.bodyEl.addClass('x-layout-tabs-body');
30245         this.panels.each(this.initPanelAsTab, this);
30246     },
30247
30248     initPanelAsTab : function(panel){
30249         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30250                     this.config.closeOnTab && panel.isClosable());
30251         if(panel.tabTip !== undefined){
30252             ti.setTooltip(panel.tabTip);
30253         }
30254         ti.on("activate", function(){
30255               this.setActivePanel(panel);
30256         }, this);
30257         if(this.config.closeOnTab){
30258             ti.on("beforeclose", function(t, e){
30259                 e.cancel = true;
30260                 this.remove(panel);
30261             }, this);
30262         }
30263         return ti;
30264     },
30265
30266     updatePanelTitle : function(panel, title){
30267         if(this.activePanel == panel){
30268             this.updateTitle(title);
30269         }
30270         if(this.tabs){
30271             var ti = this.tabs.getTab(panel.getEl().id);
30272             ti.setText(title);
30273             if(panel.tabTip !== undefined){
30274                 ti.setTooltip(panel.tabTip);
30275             }
30276         }
30277     },
30278
30279     updateTitle : function(title){
30280         if(this.titleTextEl && !this.config.title){
30281             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30282         }
30283     },
30284
30285     setActivePanel : function(panel){
30286         panel = this.getPanel(panel);
30287         if(this.activePanel && this.activePanel != panel){
30288             this.activePanel.setActiveState(false);
30289         }
30290         this.activePanel = panel;
30291         panel.setActiveState(true);
30292         if(this.panelSize){
30293             panel.setSize(this.panelSize.width, this.panelSize.height);
30294         }
30295         if(this.closeBtn){
30296             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30297         }
30298         this.updateTitle(panel.getTitle());
30299         if(this.tabs){
30300             this.fireEvent("invalidated", this);
30301         }
30302         this.fireEvent("panelactivated", this, panel);
30303     },
30304
30305     /**
30306      * Shows the specified panel.
30307      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30308      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30309      */
30310     showPanel : function(panel)
30311     {
30312         panel = this.getPanel(panel);
30313         if(panel){
30314             if(this.tabs){
30315                 var tab = this.tabs.getTab(panel.getEl().id);
30316                 if(tab.isHidden()){
30317                     this.tabs.unhideTab(tab.id);
30318                 }
30319                 tab.activate();
30320             }else{
30321                 this.setActivePanel(panel);
30322             }
30323         }
30324         return panel;
30325     },
30326
30327     /**
30328      * Get the active panel for this region.
30329      * @return {Roo.ContentPanel} The active panel or null
30330      */
30331     getActivePanel : function(){
30332         return this.activePanel;
30333     },
30334
30335     validateVisibility : function(){
30336         if(this.panels.getCount() < 1){
30337             this.updateTitle("&#160;");
30338             this.closeBtn.hide();
30339             this.hide();
30340         }else{
30341             if(!this.isVisible()){
30342                 this.show();
30343             }
30344         }
30345     },
30346
30347     /**
30348      * Adds the passed ContentPanel(s) to this region.
30349      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30350      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30351      */
30352     add : function(panel){
30353         if(arguments.length > 1){
30354             for(var i = 0, len = arguments.length; i < len; i++) {
30355                 this.add(arguments[i]);
30356             }
30357             return null;
30358         }
30359         if(this.hasPanel(panel)){
30360             this.showPanel(panel);
30361             return panel;
30362         }
30363         panel.setRegion(this);
30364         this.panels.add(panel);
30365         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30366             this.bodyEl.dom.appendChild(panel.getEl().dom);
30367             if(panel.background !== true){
30368                 this.setActivePanel(panel);
30369             }
30370             this.fireEvent("paneladded", this, panel);
30371             return panel;
30372         }
30373         if(!this.tabs){
30374             this.initTabs();
30375         }else{
30376             this.initPanelAsTab(panel);
30377         }
30378         if(panel.background !== true){
30379             this.tabs.activate(panel.getEl().id);
30380         }
30381         this.fireEvent("paneladded", this, panel);
30382         return panel;
30383     },
30384
30385     /**
30386      * Hides the tab for the specified panel.
30387      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30388      */
30389     hidePanel : function(panel){
30390         if(this.tabs && (panel = this.getPanel(panel))){
30391             this.tabs.hideTab(panel.getEl().id);
30392         }
30393     },
30394
30395     /**
30396      * Unhides the tab for a previously hidden panel.
30397      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30398      */
30399     unhidePanel : function(panel){
30400         if(this.tabs && (panel = this.getPanel(panel))){
30401             this.tabs.unhideTab(panel.getEl().id);
30402         }
30403     },
30404
30405     clearPanels : function(){
30406         while(this.panels.getCount() > 0){
30407              this.remove(this.panels.first());
30408         }
30409     },
30410
30411     /**
30412      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30413      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30414      * @param {Boolean} preservePanel Overrides the config preservePanel option
30415      * @return {Roo.ContentPanel} The panel that was removed
30416      */
30417     remove : function(panel, preservePanel){
30418         panel = this.getPanel(panel);
30419         if(!panel){
30420             return null;
30421         }
30422         var e = {};
30423         this.fireEvent("beforeremove", this, panel, e);
30424         if(e.cancel === true){
30425             return null;
30426         }
30427         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30428         var panelId = panel.getId();
30429         this.panels.removeKey(panelId);
30430         if(preservePanel){
30431             document.body.appendChild(panel.getEl().dom);
30432         }
30433         if(this.tabs){
30434             this.tabs.removeTab(panel.getEl().id);
30435         }else if (!preservePanel){
30436             this.bodyEl.dom.removeChild(panel.getEl().dom);
30437         }
30438         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30439             var p = this.panels.first();
30440             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30441             tempEl.appendChild(p.getEl().dom);
30442             this.bodyEl.update("");
30443             this.bodyEl.dom.appendChild(p.getEl().dom);
30444             tempEl = null;
30445             this.updateTitle(p.getTitle());
30446             this.tabs = null;
30447             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30448             this.setActivePanel(p);
30449         }
30450         panel.setRegion(null);
30451         if(this.activePanel == panel){
30452             this.activePanel = null;
30453         }
30454         if(this.config.autoDestroy !== false && preservePanel !== true){
30455             try{panel.destroy();}catch(e){}
30456         }
30457         this.fireEvent("panelremoved", this, panel);
30458         return panel;
30459     },
30460
30461     /**
30462      * Returns the TabPanel component used by this region
30463      * @return {Roo.TabPanel}
30464      */
30465     getTabs : function(){
30466         return this.tabs;
30467     },
30468
30469     createTool : function(parentEl, className){
30470         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30471             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30472         btn.addClassOnOver("x-layout-tools-button-over");
30473         return btn;
30474     }
30475 });/*
30476  * Based on:
30477  * Ext JS Library 1.1.1
30478  * Copyright(c) 2006-2007, Ext JS, LLC.
30479  *
30480  * Originally Released Under LGPL - original licence link has changed is not relivant.
30481  *
30482  * Fork - LGPL
30483  * <script type="text/javascript">
30484  */
30485  
30486
30487
30488 /**
30489  * @class Roo.SplitLayoutRegion
30490  * @extends Roo.LayoutRegion
30491  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30492  */
30493 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30494     this.cursor = cursor;
30495     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30496 };
30497
30498 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30499     splitTip : "Drag to resize.",
30500     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30501     useSplitTips : false,
30502
30503     applyConfig : function(config){
30504         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30505         if(config.split){
30506             if(!this.split){
30507                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30508                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30509                 /** The SplitBar for this region 
30510                 * @type Roo.SplitBar */
30511                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30512                 this.split.on("moved", this.onSplitMove, this);
30513                 this.split.useShim = config.useShim === true;
30514                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30515                 if(this.useSplitTips){
30516                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30517                 }
30518                 if(config.collapsible){
30519                     this.split.el.on("dblclick", this.collapse,  this);
30520                 }
30521             }
30522             if(typeof config.minSize != "undefined"){
30523                 this.split.minSize = config.minSize;
30524             }
30525             if(typeof config.maxSize != "undefined"){
30526                 this.split.maxSize = config.maxSize;
30527             }
30528             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30529                 this.hideSplitter();
30530             }
30531         }
30532     },
30533
30534     getHMaxSize : function(){
30535          var cmax = this.config.maxSize || 10000;
30536          var center = this.mgr.getRegion("center");
30537          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30538     },
30539
30540     getVMaxSize : function(){
30541          var cmax = this.config.maxSize || 10000;
30542          var center = this.mgr.getRegion("center");
30543          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30544     },
30545
30546     onSplitMove : function(split, newSize){
30547         this.fireEvent("resized", this, newSize);
30548     },
30549     
30550     /** 
30551      * Returns the {@link Roo.SplitBar} for this region.
30552      * @return {Roo.SplitBar}
30553      */
30554     getSplitBar : function(){
30555         return this.split;
30556     },
30557     
30558     hide : function(){
30559         this.hideSplitter();
30560         Roo.SplitLayoutRegion.superclass.hide.call(this);
30561     },
30562
30563     hideSplitter : function(){
30564         if(this.split){
30565             this.split.el.setLocation(-2000,-2000);
30566             this.split.el.hide();
30567         }
30568     },
30569
30570     show : function(){
30571         if(this.split){
30572             this.split.el.show();
30573         }
30574         Roo.SplitLayoutRegion.superclass.show.call(this);
30575     },
30576     
30577     beforeSlide: function(){
30578         if(Roo.isGecko){// firefox overflow auto bug workaround
30579             this.bodyEl.clip();
30580             if(this.tabs) {
30581                 this.tabs.bodyEl.clip();
30582             }
30583             if(this.activePanel){
30584                 this.activePanel.getEl().clip();
30585                 
30586                 if(this.activePanel.beforeSlide){
30587                     this.activePanel.beforeSlide();
30588                 }
30589             }
30590         }
30591     },
30592     
30593     afterSlide : function(){
30594         if(Roo.isGecko){// firefox overflow auto bug workaround
30595             this.bodyEl.unclip();
30596             if(this.tabs) {
30597                 this.tabs.bodyEl.unclip();
30598             }
30599             if(this.activePanel){
30600                 this.activePanel.getEl().unclip();
30601                 if(this.activePanel.afterSlide){
30602                     this.activePanel.afterSlide();
30603                 }
30604             }
30605         }
30606     },
30607
30608     initAutoHide : function(){
30609         if(this.autoHide !== false){
30610             if(!this.autoHideHd){
30611                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30612                 this.autoHideHd = {
30613                     "mouseout": function(e){
30614                         if(!e.within(this.el, true)){
30615                             st.delay(500);
30616                         }
30617                     },
30618                     "mouseover" : function(e){
30619                         st.cancel();
30620                     },
30621                     scope : this
30622                 };
30623             }
30624             this.el.on(this.autoHideHd);
30625         }
30626     },
30627
30628     clearAutoHide : function(){
30629         if(this.autoHide !== false){
30630             this.el.un("mouseout", this.autoHideHd.mouseout);
30631             this.el.un("mouseover", this.autoHideHd.mouseover);
30632         }
30633     },
30634
30635     clearMonitor : function(){
30636         Roo.get(document).un("click", this.slideInIf, this);
30637     },
30638
30639     // these names are backwards but not changed for compat
30640     slideOut : function(){
30641         if(this.isSlid || this.el.hasActiveFx()){
30642             return;
30643         }
30644         this.isSlid = true;
30645         if(this.collapseBtn){
30646             this.collapseBtn.hide();
30647         }
30648         this.closeBtnState = this.closeBtn.getStyle('display');
30649         this.closeBtn.hide();
30650         if(this.stickBtn){
30651             this.stickBtn.show();
30652         }
30653         this.el.show();
30654         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30655         this.beforeSlide();
30656         this.el.setStyle("z-index", 10001);
30657         this.el.slideIn(this.getSlideAnchor(), {
30658             callback: function(){
30659                 this.afterSlide();
30660                 this.initAutoHide();
30661                 Roo.get(document).on("click", this.slideInIf, this);
30662                 this.fireEvent("slideshow", this);
30663             },
30664             scope: this,
30665             block: true
30666         });
30667     },
30668
30669     afterSlideIn : function(){
30670         this.clearAutoHide();
30671         this.isSlid = false;
30672         this.clearMonitor();
30673         this.el.setStyle("z-index", "");
30674         if(this.collapseBtn){
30675             this.collapseBtn.show();
30676         }
30677         this.closeBtn.setStyle('display', this.closeBtnState);
30678         if(this.stickBtn){
30679             this.stickBtn.hide();
30680         }
30681         this.fireEvent("slidehide", this);
30682     },
30683
30684     slideIn : function(cb){
30685         if(!this.isSlid || this.el.hasActiveFx()){
30686             Roo.callback(cb);
30687             return;
30688         }
30689         this.isSlid = false;
30690         this.beforeSlide();
30691         this.el.slideOut(this.getSlideAnchor(), {
30692             callback: function(){
30693                 this.el.setLeftTop(-10000, -10000);
30694                 this.afterSlide();
30695                 this.afterSlideIn();
30696                 Roo.callback(cb);
30697             },
30698             scope: this,
30699             block: true
30700         });
30701     },
30702     
30703     slideInIf : function(e){
30704         if(!e.within(this.el)){
30705             this.slideIn();
30706         }
30707     },
30708
30709     animateCollapse : function(){
30710         this.beforeSlide();
30711         this.el.setStyle("z-index", 20000);
30712         var anchor = this.getSlideAnchor();
30713         this.el.slideOut(anchor, {
30714             callback : function(){
30715                 this.el.setStyle("z-index", "");
30716                 this.collapsedEl.slideIn(anchor, {duration:.3});
30717                 this.afterSlide();
30718                 this.el.setLocation(-10000,-10000);
30719                 this.el.hide();
30720                 this.fireEvent("collapsed", this);
30721             },
30722             scope: this,
30723             block: true
30724         });
30725     },
30726
30727     animateExpand : function(){
30728         this.beforeSlide();
30729         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30730         this.el.setStyle("z-index", 20000);
30731         this.collapsedEl.hide({
30732             duration:.1
30733         });
30734         this.el.slideIn(this.getSlideAnchor(), {
30735             callback : function(){
30736                 this.el.setStyle("z-index", "");
30737                 this.afterSlide();
30738                 if(this.split){
30739                     this.split.el.show();
30740                 }
30741                 this.fireEvent("invalidated", this);
30742                 this.fireEvent("expanded", this);
30743             },
30744             scope: this,
30745             block: true
30746         });
30747     },
30748
30749     anchors : {
30750         "west" : "left",
30751         "east" : "right",
30752         "north" : "top",
30753         "south" : "bottom"
30754     },
30755
30756     sanchors : {
30757         "west" : "l",
30758         "east" : "r",
30759         "north" : "t",
30760         "south" : "b"
30761     },
30762
30763     canchors : {
30764         "west" : "tl-tr",
30765         "east" : "tr-tl",
30766         "north" : "tl-bl",
30767         "south" : "bl-tl"
30768     },
30769
30770     getAnchor : function(){
30771         return this.anchors[this.position];
30772     },
30773
30774     getCollapseAnchor : function(){
30775         return this.canchors[this.position];
30776     },
30777
30778     getSlideAnchor : function(){
30779         return this.sanchors[this.position];
30780     },
30781
30782     getAlignAdj : function(){
30783         var cm = this.cmargins;
30784         switch(this.position){
30785             case "west":
30786                 return [0, 0];
30787             break;
30788             case "east":
30789                 return [0, 0];
30790             break;
30791             case "north":
30792                 return [0, 0];
30793             break;
30794             case "south":
30795                 return [0, 0];
30796             break;
30797         }
30798     },
30799
30800     getExpandAdj : function(){
30801         var c = this.collapsedEl, cm = this.cmargins;
30802         switch(this.position){
30803             case "west":
30804                 return [-(cm.right+c.getWidth()+cm.left), 0];
30805             break;
30806             case "east":
30807                 return [cm.right+c.getWidth()+cm.left, 0];
30808             break;
30809             case "north":
30810                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30811             break;
30812             case "south":
30813                 return [0, cm.top+cm.bottom+c.getHeight()];
30814             break;
30815         }
30816     }
30817 });/*
30818  * Based on:
30819  * Ext JS Library 1.1.1
30820  * Copyright(c) 2006-2007, Ext JS, LLC.
30821  *
30822  * Originally Released Under LGPL - original licence link has changed is not relivant.
30823  *
30824  * Fork - LGPL
30825  * <script type="text/javascript">
30826  */
30827 /*
30828  * These classes are private internal classes
30829  */
30830 Roo.CenterLayoutRegion = function(mgr, config){
30831     Roo.LayoutRegion.call(this, mgr, config, "center");
30832     this.visible = true;
30833     this.minWidth = config.minWidth || 20;
30834     this.minHeight = config.minHeight || 20;
30835 };
30836
30837 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30838     hide : function(){
30839         // center panel can't be hidden
30840     },
30841     
30842     show : function(){
30843         // center panel can't be hidden
30844     },
30845     
30846     getMinWidth: function(){
30847         return this.minWidth;
30848     },
30849     
30850     getMinHeight: function(){
30851         return this.minHeight;
30852     }
30853 });
30854
30855
30856 Roo.NorthLayoutRegion = function(mgr, config){
30857     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30858     if(this.split){
30859         this.split.placement = Roo.SplitBar.TOP;
30860         this.split.orientation = Roo.SplitBar.VERTICAL;
30861         this.split.el.addClass("x-layout-split-v");
30862     }
30863     var size = config.initialSize || config.height;
30864     if(typeof size != "undefined"){
30865         this.el.setHeight(size);
30866     }
30867 };
30868 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30869     orientation: Roo.SplitBar.VERTICAL,
30870     getBox : function(){
30871         if(this.collapsed){
30872             return this.collapsedEl.getBox();
30873         }
30874         var box = this.el.getBox();
30875         if(this.split){
30876             box.height += this.split.el.getHeight();
30877         }
30878         return box;
30879     },
30880     
30881     updateBox : function(box){
30882         if(this.split && !this.collapsed){
30883             box.height -= this.split.el.getHeight();
30884             this.split.el.setLeft(box.x);
30885             this.split.el.setTop(box.y+box.height);
30886             this.split.el.setWidth(box.width);
30887         }
30888         if(this.collapsed){
30889             this.updateBody(box.width, null);
30890         }
30891         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30892     }
30893 });
30894
30895 Roo.SouthLayoutRegion = function(mgr, config){
30896     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30897     if(this.split){
30898         this.split.placement = Roo.SplitBar.BOTTOM;
30899         this.split.orientation = Roo.SplitBar.VERTICAL;
30900         this.split.el.addClass("x-layout-split-v");
30901     }
30902     var size = config.initialSize || config.height;
30903     if(typeof size != "undefined"){
30904         this.el.setHeight(size);
30905     }
30906 };
30907 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30908     orientation: Roo.SplitBar.VERTICAL,
30909     getBox : function(){
30910         if(this.collapsed){
30911             return this.collapsedEl.getBox();
30912         }
30913         var box = this.el.getBox();
30914         if(this.split){
30915             var sh = this.split.el.getHeight();
30916             box.height += sh;
30917             box.y -= sh;
30918         }
30919         return box;
30920     },
30921     
30922     updateBox : function(box){
30923         if(this.split && !this.collapsed){
30924             var sh = this.split.el.getHeight();
30925             box.height -= sh;
30926             box.y += sh;
30927             this.split.el.setLeft(box.x);
30928             this.split.el.setTop(box.y-sh);
30929             this.split.el.setWidth(box.width);
30930         }
30931         if(this.collapsed){
30932             this.updateBody(box.width, null);
30933         }
30934         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30935     }
30936 });
30937
30938 Roo.EastLayoutRegion = function(mgr, config){
30939     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30940     if(this.split){
30941         this.split.placement = Roo.SplitBar.RIGHT;
30942         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30943         this.split.el.addClass("x-layout-split-h");
30944     }
30945     var size = config.initialSize || config.width;
30946     if(typeof size != "undefined"){
30947         this.el.setWidth(size);
30948     }
30949 };
30950 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30951     orientation: Roo.SplitBar.HORIZONTAL,
30952     getBox : function(){
30953         if(this.collapsed){
30954             return this.collapsedEl.getBox();
30955         }
30956         var box = this.el.getBox();
30957         if(this.split){
30958             var sw = this.split.el.getWidth();
30959             box.width += sw;
30960             box.x -= sw;
30961         }
30962         return box;
30963     },
30964
30965     updateBox : function(box){
30966         if(this.split && !this.collapsed){
30967             var sw = this.split.el.getWidth();
30968             box.width -= sw;
30969             this.split.el.setLeft(box.x);
30970             this.split.el.setTop(box.y);
30971             this.split.el.setHeight(box.height);
30972             box.x += sw;
30973         }
30974         if(this.collapsed){
30975             this.updateBody(null, box.height);
30976         }
30977         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30978     }
30979 });
30980
30981 Roo.WestLayoutRegion = function(mgr, config){
30982     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30983     if(this.split){
30984         this.split.placement = Roo.SplitBar.LEFT;
30985         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30986         this.split.el.addClass("x-layout-split-h");
30987     }
30988     var size = config.initialSize || config.width;
30989     if(typeof size != "undefined"){
30990         this.el.setWidth(size);
30991     }
30992 };
30993 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30994     orientation: Roo.SplitBar.HORIZONTAL,
30995     getBox : function(){
30996         if(this.collapsed){
30997             return this.collapsedEl.getBox();
30998         }
30999         var box = this.el.getBox();
31000         if(this.split){
31001             box.width += this.split.el.getWidth();
31002         }
31003         return box;
31004     },
31005     
31006     updateBox : function(box){
31007         if(this.split && !this.collapsed){
31008             var sw = this.split.el.getWidth();
31009             box.width -= sw;
31010             this.split.el.setLeft(box.x+box.width);
31011             this.split.el.setTop(box.y);
31012             this.split.el.setHeight(box.height);
31013         }
31014         if(this.collapsed){
31015             this.updateBody(null, box.height);
31016         }
31017         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31018     }
31019 });
31020 /*
31021  * Based on:
31022  * Ext JS Library 1.1.1
31023  * Copyright(c) 2006-2007, Ext JS, LLC.
31024  *
31025  * Originally Released Under LGPL - original licence link has changed is not relivant.
31026  *
31027  * Fork - LGPL
31028  * <script type="text/javascript">
31029  */
31030  
31031  
31032 /*
31033  * Private internal class for reading and applying state
31034  */
31035 Roo.LayoutStateManager = function(layout){
31036      // default empty state
31037      this.state = {
31038         north: {},
31039         south: {},
31040         east: {},
31041         west: {}       
31042     };
31043 };
31044
31045 Roo.LayoutStateManager.prototype = {
31046     init : function(layout, provider){
31047         this.provider = provider;
31048         var state = provider.get(layout.id+"-layout-state");
31049         if(state){
31050             var wasUpdating = layout.isUpdating();
31051             if(!wasUpdating){
31052                 layout.beginUpdate();
31053             }
31054             for(var key in state){
31055                 if(typeof state[key] != "function"){
31056                     var rstate = state[key];
31057                     var r = layout.getRegion(key);
31058                     if(r && rstate){
31059                         if(rstate.size){
31060                             r.resizeTo(rstate.size);
31061                         }
31062                         if(rstate.collapsed == true){
31063                             r.collapse(true);
31064                         }else{
31065                             r.expand(null, true);
31066                         }
31067                     }
31068                 }
31069             }
31070             if(!wasUpdating){
31071                 layout.endUpdate();
31072             }
31073             this.state = state; 
31074         }
31075         this.layout = layout;
31076         layout.on("regionresized", this.onRegionResized, this);
31077         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31078         layout.on("regionexpanded", this.onRegionExpanded, this);
31079     },
31080     
31081     storeState : function(){
31082         this.provider.set(this.layout.id+"-layout-state", this.state);
31083     },
31084     
31085     onRegionResized : function(region, newSize){
31086         this.state[region.getPosition()].size = newSize;
31087         this.storeState();
31088     },
31089     
31090     onRegionCollapsed : function(region){
31091         this.state[region.getPosition()].collapsed = true;
31092         this.storeState();
31093     },
31094     
31095     onRegionExpanded : function(region){
31096         this.state[region.getPosition()].collapsed = false;
31097         this.storeState();
31098     }
31099 };/*
31100  * Based on:
31101  * Ext JS Library 1.1.1
31102  * Copyright(c) 2006-2007, Ext JS, LLC.
31103  *
31104  * Originally Released Under LGPL - original licence link has changed is not relivant.
31105  *
31106  * Fork - LGPL
31107  * <script type="text/javascript">
31108  */
31109 /**
31110  * @class Roo.ContentPanel
31111  * @extends Roo.util.Observable
31112  * A basic ContentPanel element.
31113  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31114  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31115  * @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
31116  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31117  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31118  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31119  * @cfg {Toolbar}   toolbar       A toolbar for this panel
31120  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31121  * @cfg {String} title          The title for this panel
31122  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31123  * @cfg {String} url            Calls {@link #setUrl} with this value
31124  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31125  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31126  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31127  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31128
31129  * @constructor
31130  * Create a new ContentPanel.
31131  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31132  * @param {String/Object} config A string to set only the title or a config object
31133  * @param {String} content (optional) Set the HTML content for this panel
31134  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31135  */
31136 Roo.ContentPanel = function(el, config, content){
31137     
31138      
31139     /*
31140     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31141         config = el;
31142         el = Roo.id();
31143     }
31144     if (config && config.parentLayout) { 
31145         el = config.parentLayout.el.createChild(); 
31146     }
31147     */
31148     if(el.autoCreate){ // xtype is available if this is called from factory
31149         config = el;
31150         el = Roo.id();
31151     }
31152     this.el = Roo.get(el);
31153     if(!this.el && config && config.autoCreate){
31154         if(typeof config.autoCreate == "object"){
31155             if(!config.autoCreate.id){
31156                 config.autoCreate.id = config.id||el;
31157             }
31158             this.el = Roo.DomHelper.append(document.body,
31159                         config.autoCreate, true);
31160         }else{
31161             this.el = Roo.DomHelper.append(document.body,
31162                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31163         }
31164     }
31165     this.closable = false;
31166     this.loaded = false;
31167     this.active = false;
31168     if(typeof config == "string"){
31169         this.title = config;
31170     }else{
31171         Roo.apply(this, config);
31172     }
31173     
31174     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31175         this.wrapEl = this.el.wrap();
31176         this.toolbar.container = this.el.insertSibling(false, 'before');
31177         this.toolbar = new Roo.Toolbar(this.toolbar);
31178     }
31179     
31180     // xtype created footer. - not sure if will work as we normally have to render first..
31181     if (this.footer && !this.footer.el && this.footer.xtype) {
31182         if (!this.wrapEl) {
31183             this.wrapEl = this.el.wrap();
31184         }
31185     
31186         this.footer.container = this.wrapEl.createChild();
31187          
31188         this.footer = Roo.factory(this.footer, Roo);
31189         
31190     }
31191     
31192     if(this.resizeEl){
31193         this.resizeEl = Roo.get(this.resizeEl, true);
31194     }else{
31195         this.resizeEl = this.el;
31196     }
31197     // handle view.xtype
31198     
31199  
31200     
31201     
31202     this.addEvents({
31203         /**
31204          * @event activate
31205          * Fires when this panel is activated. 
31206          * @param {Roo.ContentPanel} this
31207          */
31208         "activate" : true,
31209         /**
31210          * @event deactivate
31211          * Fires when this panel is activated. 
31212          * @param {Roo.ContentPanel} this
31213          */
31214         "deactivate" : true,
31215
31216         /**
31217          * @event resize
31218          * Fires when this panel is resized if fitToFrame is true.
31219          * @param {Roo.ContentPanel} this
31220          * @param {Number} width The width after any component adjustments
31221          * @param {Number} height The height after any component adjustments
31222          */
31223         "resize" : true,
31224         
31225          /**
31226          * @event render
31227          * Fires when this tab is created
31228          * @param {Roo.ContentPanel} this
31229          */
31230         "render" : true
31231          
31232         
31233     });
31234     
31235
31236     
31237     
31238     if(this.autoScroll){
31239         this.resizeEl.setStyle("overflow", "auto");
31240     } else {
31241         // fix randome scrolling
31242         this.el.on('scroll', function() {
31243             Roo.log('fix random scolling');
31244             this.scrollTo('top',0); 
31245         });
31246     }
31247     content = content || this.content;
31248     if(content){
31249         this.setContent(content);
31250     }
31251     if(config && config.url){
31252         this.setUrl(this.url, this.params, this.loadOnce);
31253     }
31254     
31255     
31256     
31257     Roo.ContentPanel.superclass.constructor.call(this);
31258     
31259     if (this.view && typeof(this.view.xtype) != 'undefined') {
31260         this.view.el = this.el.appendChild(document.createElement("div"));
31261         this.view = Roo.factory(this.view); 
31262         this.view.render  &&  this.view.render(false, '');  
31263     }
31264     
31265     
31266     this.fireEvent('render', this);
31267 };
31268
31269 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31270     tabTip:'',
31271     setRegion : function(region){
31272         this.region = region;
31273         if(region){
31274            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31275         }else{
31276            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31277         } 
31278     },
31279     
31280     /**
31281      * Returns the toolbar for this Panel if one was configured. 
31282      * @return {Roo.Toolbar} 
31283      */
31284     getToolbar : function(){
31285         return this.toolbar;
31286     },
31287     
31288     setActiveState : function(active){
31289         this.active = active;
31290         if(!active){
31291             this.fireEvent("deactivate", this);
31292         }else{
31293             this.fireEvent("activate", this);
31294         }
31295     },
31296     /**
31297      * Updates this panel's element
31298      * @param {String} content The new content
31299      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31300     */
31301     setContent : function(content, loadScripts){
31302         this.el.update(content, loadScripts);
31303     },
31304
31305     ignoreResize : function(w, h){
31306         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31307             return true;
31308         }else{
31309             this.lastSize = {width: w, height: h};
31310             return false;
31311         }
31312     },
31313     /**
31314      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31315      * @return {Roo.UpdateManager} The UpdateManager
31316      */
31317     getUpdateManager : function(){
31318         return this.el.getUpdateManager();
31319     },
31320      /**
31321      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31322      * @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:
31323 <pre><code>
31324 panel.load({
31325     url: "your-url.php",
31326     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31327     callback: yourFunction,
31328     scope: yourObject, //(optional scope)
31329     discardUrl: false,
31330     nocache: false,
31331     text: "Loading...",
31332     timeout: 30,
31333     scripts: false
31334 });
31335 </code></pre>
31336      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31337      * 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.
31338      * @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}
31339      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31340      * @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.
31341      * @return {Roo.ContentPanel} this
31342      */
31343     load : function(){
31344         var um = this.el.getUpdateManager();
31345         um.update.apply(um, arguments);
31346         return this;
31347     },
31348
31349
31350     /**
31351      * 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.
31352      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31353      * @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)
31354      * @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)
31355      * @return {Roo.UpdateManager} The UpdateManager
31356      */
31357     setUrl : function(url, params, loadOnce){
31358         if(this.refreshDelegate){
31359             this.removeListener("activate", this.refreshDelegate);
31360         }
31361         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31362         this.on("activate", this.refreshDelegate);
31363         return this.el.getUpdateManager();
31364     },
31365     
31366     _handleRefresh : function(url, params, loadOnce){
31367         if(!loadOnce || !this.loaded){
31368             var updater = this.el.getUpdateManager();
31369             updater.update(url, params, this._setLoaded.createDelegate(this));
31370         }
31371     },
31372     
31373     _setLoaded : function(){
31374         this.loaded = true;
31375     }, 
31376     
31377     /**
31378      * Returns this panel's id
31379      * @return {String} 
31380      */
31381     getId : function(){
31382         return this.el.id;
31383     },
31384     
31385     /** 
31386      * Returns this panel's element - used by regiosn to add.
31387      * @return {Roo.Element} 
31388      */
31389     getEl : function(){
31390         return this.wrapEl || this.el;
31391     },
31392     
31393     adjustForComponents : function(width, height)
31394     {
31395         //Roo.log('adjustForComponents ');
31396         if(this.resizeEl != this.el){
31397             width -= this.el.getFrameWidth('lr');
31398             height -= this.el.getFrameWidth('tb');
31399         }
31400         if(this.toolbar){
31401             var te = this.toolbar.getEl();
31402             height -= te.getHeight();
31403             te.setWidth(width);
31404         }
31405         if(this.footer){
31406             var te = this.footer.getEl();
31407             //Roo.log("footer:" + te.getHeight());
31408             
31409             height -= te.getHeight();
31410             te.setWidth(width);
31411         }
31412         
31413         
31414         if(this.adjustments){
31415             width += this.adjustments[0];
31416             height += this.adjustments[1];
31417         }
31418         return {"width": width, "height": height};
31419     },
31420     
31421     setSize : function(width, height){
31422         if(this.fitToFrame && !this.ignoreResize(width, height)){
31423             if(this.fitContainer && this.resizeEl != this.el){
31424                 this.el.setSize(width, height);
31425             }
31426             var size = this.adjustForComponents(width, height);
31427             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31428             this.fireEvent('resize', this, size.width, size.height);
31429         }
31430     },
31431     
31432     /**
31433      * Returns this panel's title
31434      * @return {String} 
31435      */
31436     getTitle : function(){
31437         return this.title;
31438     },
31439     
31440     /**
31441      * Set this panel's title
31442      * @param {String} title
31443      */
31444     setTitle : function(title){
31445         this.title = title;
31446         if(this.region){
31447             this.region.updatePanelTitle(this, title);
31448         }
31449     },
31450     
31451     /**
31452      * Returns true is this panel was configured to be closable
31453      * @return {Boolean} 
31454      */
31455     isClosable : function(){
31456         return this.closable;
31457     },
31458     
31459     beforeSlide : function(){
31460         this.el.clip();
31461         this.resizeEl.clip();
31462     },
31463     
31464     afterSlide : function(){
31465         this.el.unclip();
31466         this.resizeEl.unclip();
31467     },
31468     
31469     /**
31470      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31471      *   Will fail silently if the {@link #setUrl} method has not been called.
31472      *   This does not activate the panel, just updates its content.
31473      */
31474     refresh : function(){
31475         if(this.refreshDelegate){
31476            this.loaded = false;
31477            this.refreshDelegate();
31478         }
31479     },
31480     
31481     /**
31482      * Destroys this panel
31483      */
31484     destroy : function(){
31485         this.el.removeAllListeners();
31486         var tempEl = document.createElement("span");
31487         tempEl.appendChild(this.el.dom);
31488         tempEl.innerHTML = "";
31489         this.el.remove();
31490         this.el = null;
31491     },
31492     
31493     /**
31494      * form - if the content panel contains a form - this is a reference to it.
31495      * @type {Roo.form.Form}
31496      */
31497     form : false,
31498     /**
31499      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31500      *    This contains a reference to it.
31501      * @type {Roo.View}
31502      */
31503     view : false,
31504     
31505       /**
31506      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31507      * <pre><code>
31508
31509 layout.addxtype({
31510        xtype : 'Form',
31511        items: [ .... ]
31512    }
31513 );
31514
31515 </code></pre>
31516      * @param {Object} cfg Xtype definition of item to add.
31517      */
31518     
31519     addxtype : function(cfg) {
31520         // add form..
31521         if (cfg.xtype.match(/^Form$/)) {
31522             
31523             var el;
31524             //if (this.footer) {
31525             //    el = this.footer.container.insertSibling(false, 'before');
31526             //} else {
31527                 el = this.el.createChild();
31528             //}
31529
31530             this.form = new  Roo.form.Form(cfg);
31531             
31532             
31533             if ( this.form.allItems.length) {
31534                 this.form.render(el.dom);
31535             }
31536             return this.form;
31537         }
31538         // should only have one of theses..
31539         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31540             // views.. should not be just added - used named prop 'view''
31541             
31542             cfg.el = this.el.appendChild(document.createElement("div"));
31543             // factory?
31544             
31545             var ret = new Roo.factory(cfg);
31546              
31547              ret.render && ret.render(false, ''); // render blank..
31548             this.view = ret;
31549             return ret;
31550         }
31551         return false;
31552     }
31553 });
31554
31555 /**
31556  * @class Roo.GridPanel
31557  * @extends Roo.ContentPanel
31558  * @constructor
31559  * Create a new GridPanel.
31560  * @param {Roo.grid.Grid} grid The grid for this panel
31561  * @param {String/Object} config A string to set only the panel's title, or a config object
31562  */
31563 Roo.GridPanel = function(grid, config){
31564     
31565   
31566     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31567         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31568         
31569     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31570     
31571     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31572     
31573     if(this.toolbar){
31574         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31575     }
31576     // xtype created footer. - not sure if will work as we normally have to render first..
31577     if (this.footer && !this.footer.el && this.footer.xtype) {
31578         
31579         this.footer.container = this.grid.getView().getFooterPanel(true);
31580         this.footer.dataSource = this.grid.dataSource;
31581         this.footer = Roo.factory(this.footer, Roo);
31582         
31583     }
31584     
31585     grid.monitorWindowResize = false; // turn off autosizing
31586     grid.autoHeight = false;
31587     grid.autoWidth = false;
31588     this.grid = grid;
31589     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31590 };
31591
31592 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31593     getId : function(){
31594         return this.grid.id;
31595     },
31596     
31597     /**
31598      * Returns the grid for this panel
31599      * @return {Roo.grid.Grid} 
31600      */
31601     getGrid : function(){
31602         return this.grid;    
31603     },
31604     
31605     setSize : function(width, height){
31606         if(!this.ignoreResize(width, height)){
31607             var grid = this.grid;
31608             var size = this.adjustForComponents(width, height);
31609             grid.getGridEl().setSize(size.width, size.height);
31610             grid.autoSize();
31611         }
31612     },
31613     
31614     beforeSlide : function(){
31615         this.grid.getView().scroller.clip();
31616     },
31617     
31618     afterSlide : function(){
31619         this.grid.getView().scroller.unclip();
31620     },
31621     
31622     destroy : function(){
31623         this.grid.destroy();
31624         delete this.grid;
31625         Roo.GridPanel.superclass.destroy.call(this); 
31626     }
31627 });
31628
31629
31630 /**
31631  * @class Roo.NestedLayoutPanel
31632  * @extends Roo.ContentPanel
31633  * @constructor
31634  * Create a new NestedLayoutPanel.
31635  * 
31636  * 
31637  * @param {Roo.BorderLayout} layout The layout for this panel
31638  * @param {String/Object} config A string to set only the title or a config object
31639  */
31640 Roo.NestedLayoutPanel = function(layout, config)
31641 {
31642     // construct with only one argument..
31643     /* FIXME - implement nicer consturctors
31644     if (layout.layout) {
31645         config = layout;
31646         layout = config.layout;
31647         delete config.layout;
31648     }
31649     if (layout.xtype && !layout.getEl) {
31650         // then layout needs constructing..
31651         layout = Roo.factory(layout, Roo);
31652     }
31653     */
31654     
31655     
31656     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31657     
31658     layout.monitorWindowResize = false; // turn off autosizing
31659     this.layout = layout;
31660     this.layout.getEl().addClass("x-layout-nested-layout");
31661     
31662     
31663     
31664     
31665 };
31666
31667 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31668
31669     setSize : function(width, height){
31670         if(!this.ignoreResize(width, height)){
31671             var size = this.adjustForComponents(width, height);
31672             var el = this.layout.getEl();
31673             el.setSize(size.width, size.height);
31674             var touch = el.dom.offsetWidth;
31675             this.layout.layout();
31676             // ie requires a double layout on the first pass
31677             if(Roo.isIE && !this.initialized){
31678                 this.initialized = true;
31679                 this.layout.layout();
31680             }
31681         }
31682     },
31683     
31684     // activate all subpanels if not currently active..
31685     
31686     setActiveState : function(active){
31687         this.active = active;
31688         if(!active){
31689             this.fireEvent("deactivate", this);
31690             return;
31691         }
31692         
31693         this.fireEvent("activate", this);
31694         // not sure if this should happen before or after..
31695         if (!this.layout) {
31696             return; // should not happen..
31697         }
31698         var reg = false;
31699         for (var r in this.layout.regions) {
31700             reg = this.layout.getRegion(r);
31701             if (reg.getActivePanel()) {
31702                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31703                 reg.setActivePanel(reg.getActivePanel());
31704                 continue;
31705             }
31706             if (!reg.panels.length) {
31707                 continue;
31708             }
31709             reg.showPanel(reg.getPanel(0));
31710         }
31711         
31712         
31713         
31714         
31715     },
31716     
31717     /**
31718      * Returns the nested BorderLayout for this panel
31719      * @return {Roo.BorderLayout} 
31720      */
31721     getLayout : function(){
31722         return this.layout;
31723     },
31724     
31725      /**
31726      * Adds a xtype elements to the layout of the nested panel
31727      * <pre><code>
31728
31729 panel.addxtype({
31730        xtype : 'ContentPanel',
31731        region: 'west',
31732        items: [ .... ]
31733    }
31734 );
31735
31736 panel.addxtype({
31737         xtype : 'NestedLayoutPanel',
31738         region: 'west',
31739         layout: {
31740            center: { },
31741            west: { }   
31742         },
31743         items : [ ... list of content panels or nested layout panels.. ]
31744    }
31745 );
31746 </code></pre>
31747      * @param {Object} cfg Xtype definition of item to add.
31748      */
31749     addxtype : function(cfg) {
31750         return this.layout.addxtype(cfg);
31751     
31752     }
31753 });
31754
31755 Roo.ScrollPanel = function(el, config, content){
31756     config = config || {};
31757     config.fitToFrame = true;
31758     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31759     
31760     this.el.dom.style.overflow = "hidden";
31761     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31762     this.el.removeClass("x-layout-inactive-content");
31763     this.el.on("mousewheel", this.onWheel, this);
31764
31765     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31766     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31767     up.unselectable(); down.unselectable();
31768     up.on("click", this.scrollUp, this);
31769     down.on("click", this.scrollDown, this);
31770     up.addClassOnOver("x-scroller-btn-over");
31771     down.addClassOnOver("x-scroller-btn-over");
31772     up.addClassOnClick("x-scroller-btn-click");
31773     down.addClassOnClick("x-scroller-btn-click");
31774     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31775
31776     this.resizeEl = this.el;
31777     this.el = wrap; this.up = up; this.down = down;
31778 };
31779
31780 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31781     increment : 100,
31782     wheelIncrement : 5,
31783     scrollUp : function(){
31784         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31785     },
31786
31787     scrollDown : function(){
31788         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31789     },
31790
31791     afterScroll : function(){
31792         var el = this.resizeEl;
31793         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31794         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31795         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31796     },
31797
31798     setSize : function(){
31799         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31800         this.afterScroll();
31801     },
31802
31803     onWheel : function(e){
31804         var d = e.getWheelDelta();
31805         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31806         this.afterScroll();
31807         e.stopEvent();
31808     },
31809
31810     setContent : function(content, loadScripts){
31811         this.resizeEl.update(content, loadScripts);
31812     }
31813
31814 });
31815
31816
31817
31818
31819
31820
31821
31822
31823
31824 /**
31825  * @class Roo.TreePanel
31826  * @extends Roo.ContentPanel
31827  * @constructor
31828  * Create a new TreePanel. - defaults to fit/scoll contents.
31829  * @param {String/Object} config A string to set only the panel's title, or a config object
31830  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31831  */
31832 Roo.TreePanel = function(config){
31833     var el = config.el;
31834     var tree = config.tree;
31835     delete config.tree; 
31836     delete config.el; // hopefull!
31837     
31838     // wrapper for IE7 strict & safari scroll issue
31839     
31840     var treeEl = el.createChild();
31841     config.resizeEl = treeEl;
31842     
31843     
31844     
31845     Roo.TreePanel.superclass.constructor.call(this, el, config);
31846  
31847  
31848     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31849     //console.log(tree);
31850     this.on('activate', function()
31851     {
31852         if (this.tree.rendered) {
31853             return;
31854         }
31855         //console.log('render tree');
31856         this.tree.render();
31857     });
31858     // this should not be needed.. - it's actually the 'el' that resizes?
31859     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31860     
31861     //this.on('resize',  function (cp, w, h) {
31862     //        this.tree.innerCt.setWidth(w);
31863     //        this.tree.innerCt.setHeight(h);
31864     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
31865     //});
31866
31867         
31868     
31869 };
31870
31871 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31872     fitToFrame : true,
31873     autoScroll : true
31874 });
31875
31876
31877
31878
31879
31880
31881
31882
31883
31884
31885
31886 /*
31887  * Based on:
31888  * Ext JS Library 1.1.1
31889  * Copyright(c) 2006-2007, Ext JS, LLC.
31890  *
31891  * Originally Released Under LGPL - original licence link has changed is not relivant.
31892  *
31893  * Fork - LGPL
31894  * <script type="text/javascript">
31895  */
31896  
31897
31898 /**
31899  * @class Roo.ReaderLayout
31900  * @extends Roo.BorderLayout
31901  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31902  * center region containing two nested regions (a top one for a list view and one for item preview below),
31903  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31904  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31905  * expedites the setup of the overall layout and regions for this common application style.
31906  * Example:
31907  <pre><code>
31908 var reader = new Roo.ReaderLayout();
31909 var CP = Roo.ContentPanel;  // shortcut for adding
31910
31911 reader.beginUpdate();
31912 reader.add("north", new CP("north", "North"));
31913 reader.add("west", new CP("west", {title: "West"}));
31914 reader.add("east", new CP("east", {title: "East"}));
31915
31916 reader.regions.listView.add(new CP("listView", "List"));
31917 reader.regions.preview.add(new CP("preview", "Preview"));
31918 reader.endUpdate();
31919 </code></pre>
31920 * @constructor
31921 * Create a new ReaderLayout
31922 * @param {Object} config Configuration options
31923 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31924 * document.body if omitted)
31925 */
31926 Roo.ReaderLayout = function(config, renderTo){
31927     var c = config || {size:{}};
31928     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31929         north: c.north !== false ? Roo.apply({
31930             split:false,
31931             initialSize: 32,
31932             titlebar: false
31933         }, c.north) : false,
31934         west: c.west !== false ? Roo.apply({
31935             split:true,
31936             initialSize: 200,
31937             minSize: 175,
31938             maxSize: 400,
31939             titlebar: true,
31940             collapsible: true,
31941             animate: true,
31942             margins:{left:5,right:0,bottom:5,top:5},
31943             cmargins:{left:5,right:5,bottom:5,top:5}
31944         }, c.west) : false,
31945         east: c.east !== false ? Roo.apply({
31946             split:true,
31947             initialSize: 200,
31948             minSize: 175,
31949             maxSize: 400,
31950             titlebar: true,
31951             collapsible: true,
31952             animate: true,
31953             margins:{left:0,right:5,bottom:5,top:5},
31954             cmargins:{left:5,right:5,bottom:5,top:5}
31955         }, c.east) : false,
31956         center: Roo.apply({
31957             tabPosition: 'top',
31958             autoScroll:false,
31959             closeOnTab: true,
31960             titlebar:false,
31961             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31962         }, c.center)
31963     });
31964
31965     this.el.addClass('x-reader');
31966
31967     this.beginUpdate();
31968
31969     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31970         south: c.preview !== false ? Roo.apply({
31971             split:true,
31972             initialSize: 200,
31973             minSize: 100,
31974             autoScroll:true,
31975             collapsible:true,
31976             titlebar: true,
31977             cmargins:{top:5,left:0, right:0, bottom:0}
31978         }, c.preview) : false,
31979         center: Roo.apply({
31980             autoScroll:false,
31981             titlebar:false,
31982             minHeight:200
31983         }, c.listView)
31984     });
31985     this.add('center', new Roo.NestedLayoutPanel(inner,
31986             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31987
31988     this.endUpdate();
31989
31990     this.regions.preview = inner.getRegion('south');
31991     this.regions.listView = inner.getRegion('center');
31992 };
31993
31994 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31995  * Based on:
31996  * Ext JS Library 1.1.1
31997  * Copyright(c) 2006-2007, Ext JS, LLC.
31998  *
31999  * Originally Released Under LGPL - original licence link has changed is not relivant.
32000  *
32001  * Fork - LGPL
32002  * <script type="text/javascript">
32003  */
32004  
32005 /**
32006  * @class Roo.grid.Grid
32007  * @extends Roo.util.Observable
32008  * This class represents the primary interface of a component based grid control.
32009  * <br><br>Usage:<pre><code>
32010  var grid = new Roo.grid.Grid("my-container-id", {
32011      ds: myDataStore,
32012      cm: myColModel,
32013      selModel: mySelectionModel,
32014      autoSizeColumns: true,
32015      monitorWindowResize: false,
32016      trackMouseOver: true
32017  });
32018  // set any options
32019  grid.render();
32020  * </code></pre>
32021  * <b>Common Problems:</b><br/>
32022  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32023  * element will correct this<br/>
32024  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32025  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32026  * are unpredictable.<br/>
32027  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32028  * grid to calculate dimensions/offsets.<br/>
32029   * @constructor
32030  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32031  * The container MUST have some type of size defined for the grid to fill. The container will be
32032  * automatically set to position relative if it isn't already.
32033  * @param {Object} config A config object that sets properties on this grid.
32034  */
32035 Roo.grid.Grid = function(container, config){
32036         // initialize the container
32037         this.container = Roo.get(container);
32038         this.container.update("");
32039         this.container.setStyle("overflow", "hidden");
32040     this.container.addClass('x-grid-container');
32041
32042     this.id = this.container.id;
32043
32044     Roo.apply(this, config);
32045     // check and correct shorthanded configs
32046     if(this.ds){
32047         this.dataSource = this.ds;
32048         delete this.ds;
32049     }
32050     if(this.cm){
32051         this.colModel = this.cm;
32052         delete this.cm;
32053     }
32054     if(this.sm){
32055         this.selModel = this.sm;
32056         delete this.sm;
32057     }
32058
32059     if (this.selModel) {
32060         this.selModel = Roo.factory(this.selModel, Roo.grid);
32061         this.sm = this.selModel;
32062         this.sm.xmodule = this.xmodule || false;
32063     }
32064     if (typeof(this.colModel.config) == 'undefined') {
32065         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32066         this.cm = this.colModel;
32067         this.cm.xmodule = this.xmodule || false;
32068     }
32069     if (this.dataSource) {
32070         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32071         this.ds = this.dataSource;
32072         this.ds.xmodule = this.xmodule || false;
32073          
32074     }
32075     
32076     
32077     
32078     if(this.width){
32079         this.container.setWidth(this.width);
32080     }
32081
32082     if(this.height){
32083         this.container.setHeight(this.height);
32084     }
32085     /** @private */
32086         this.addEvents({
32087         // raw events
32088         /**
32089          * @event click
32090          * The raw click event for the entire grid.
32091          * @param {Roo.EventObject} e
32092          */
32093         "click" : true,
32094         /**
32095          * @event dblclick
32096          * The raw dblclick event for the entire grid.
32097          * @param {Roo.EventObject} e
32098          */
32099         "dblclick" : true,
32100         /**
32101          * @event contextmenu
32102          * The raw contextmenu event for the entire grid.
32103          * @param {Roo.EventObject} e
32104          */
32105         "contextmenu" : true,
32106         /**
32107          * @event mousedown
32108          * The raw mousedown event for the entire grid.
32109          * @param {Roo.EventObject} e
32110          */
32111         "mousedown" : true,
32112         /**
32113          * @event mouseup
32114          * The raw mouseup event for the entire grid.
32115          * @param {Roo.EventObject} e
32116          */
32117         "mouseup" : true,
32118         /**
32119          * @event mouseover
32120          * The raw mouseover event for the entire grid.
32121          * @param {Roo.EventObject} e
32122          */
32123         "mouseover" : true,
32124         /**
32125          * @event mouseout
32126          * The raw mouseout event for the entire grid.
32127          * @param {Roo.EventObject} e
32128          */
32129         "mouseout" : true,
32130         /**
32131          * @event keypress
32132          * The raw keypress event for the entire grid.
32133          * @param {Roo.EventObject} e
32134          */
32135         "keypress" : true,
32136         /**
32137          * @event keydown
32138          * The raw keydown event for the entire grid.
32139          * @param {Roo.EventObject} e
32140          */
32141         "keydown" : true,
32142
32143         // custom events
32144
32145         /**
32146          * @event cellclick
32147          * Fires when a cell is clicked
32148          * @param {Grid} this
32149          * @param {Number} rowIndex
32150          * @param {Number} columnIndex
32151          * @param {Roo.EventObject} e
32152          */
32153         "cellclick" : true,
32154         /**
32155          * @event celldblclick
32156          * Fires when a cell is double clicked
32157          * @param {Grid} this
32158          * @param {Number} rowIndex
32159          * @param {Number} columnIndex
32160          * @param {Roo.EventObject} e
32161          */
32162         "celldblclick" : true,
32163         /**
32164          * @event rowclick
32165          * Fires when a row is clicked
32166          * @param {Grid} this
32167          * @param {Number} rowIndex
32168          * @param {Roo.EventObject} e
32169          */
32170         "rowclick" : true,
32171         /**
32172          * @event rowdblclick
32173          * Fires when a row is double clicked
32174          * @param {Grid} this
32175          * @param {Number} rowIndex
32176          * @param {Roo.EventObject} e
32177          */
32178         "rowdblclick" : true,
32179         /**
32180          * @event headerclick
32181          * Fires when a header is clicked
32182          * @param {Grid} this
32183          * @param {Number} columnIndex
32184          * @param {Roo.EventObject} e
32185          */
32186         "headerclick" : true,
32187         /**
32188          * @event headerdblclick
32189          * Fires when a header cell is double clicked
32190          * @param {Grid} this
32191          * @param {Number} columnIndex
32192          * @param {Roo.EventObject} e
32193          */
32194         "headerdblclick" : true,
32195         /**
32196          * @event rowcontextmenu
32197          * Fires when a row is right clicked
32198          * @param {Grid} this
32199          * @param {Number} rowIndex
32200          * @param {Roo.EventObject} e
32201          */
32202         "rowcontextmenu" : true,
32203         /**
32204          * @event cellcontextmenu
32205          * Fires when a cell is right clicked
32206          * @param {Grid} this
32207          * @param {Number} rowIndex
32208          * @param {Number} cellIndex
32209          * @param {Roo.EventObject} e
32210          */
32211          "cellcontextmenu" : true,
32212         /**
32213          * @event headercontextmenu
32214          * Fires when a header is right clicked
32215          * @param {Grid} this
32216          * @param {Number} columnIndex
32217          * @param {Roo.EventObject} e
32218          */
32219         "headercontextmenu" : true,
32220         /**
32221          * @event bodyscroll
32222          * Fires when the body element is scrolled
32223          * @param {Number} scrollLeft
32224          * @param {Number} scrollTop
32225          */
32226         "bodyscroll" : true,
32227         /**
32228          * @event columnresize
32229          * Fires when the user resizes a column
32230          * @param {Number} columnIndex
32231          * @param {Number} newSize
32232          */
32233         "columnresize" : true,
32234         /**
32235          * @event columnmove
32236          * Fires when the user moves a column
32237          * @param {Number} oldIndex
32238          * @param {Number} newIndex
32239          */
32240         "columnmove" : true,
32241         /**
32242          * @event startdrag
32243          * Fires when row(s) start being dragged
32244          * @param {Grid} this
32245          * @param {Roo.GridDD} dd The drag drop object
32246          * @param {event} e The raw browser event
32247          */
32248         "startdrag" : true,
32249         /**
32250          * @event enddrag
32251          * Fires when a drag operation is complete
32252          * @param {Grid} this
32253          * @param {Roo.GridDD} dd The drag drop object
32254          * @param {event} e The raw browser event
32255          */
32256         "enddrag" : true,
32257         /**
32258          * @event dragdrop
32259          * Fires when dragged row(s) are dropped on a valid DD target
32260          * @param {Grid} this
32261          * @param {Roo.GridDD} dd The drag drop object
32262          * @param {String} targetId The target drag drop object
32263          * @param {event} e The raw browser event
32264          */
32265         "dragdrop" : true,
32266         /**
32267          * @event dragover
32268          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32269          * @param {Grid} this
32270          * @param {Roo.GridDD} dd The drag drop object
32271          * @param {String} targetId The target drag drop object
32272          * @param {event} e The raw browser event
32273          */
32274         "dragover" : true,
32275         /**
32276          * @event dragenter
32277          *  Fires when the dragged row(s) first cross another DD target while being dragged
32278          * @param {Grid} this
32279          * @param {Roo.GridDD} dd The drag drop object
32280          * @param {String} targetId The target drag drop object
32281          * @param {event} e The raw browser event
32282          */
32283         "dragenter" : true,
32284         /**
32285          * @event dragout
32286          * Fires when the dragged row(s) leave another DD target while being dragged
32287          * @param {Grid} this
32288          * @param {Roo.GridDD} dd The drag drop object
32289          * @param {String} targetId The target drag drop object
32290          * @param {event} e The raw browser event
32291          */
32292         "dragout" : true,
32293         /**
32294          * @event rowclass
32295          * Fires when a row is rendered, so you can change add a style to it.
32296          * @param {GridView} gridview   The grid view
32297          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32298          */
32299         'rowclass' : true,
32300
32301         /**
32302          * @event render
32303          * Fires when the grid is rendered
32304          * @param {Grid} grid
32305          */
32306         'render' : true
32307     });
32308
32309     Roo.grid.Grid.superclass.constructor.call(this);
32310 };
32311 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32312     
32313     /**
32314      * @cfg {String} ddGroup - drag drop group.
32315      */
32316
32317     /**
32318      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32319      */
32320     minColumnWidth : 25,
32321
32322     /**
32323      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32324      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32325      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32326      */
32327     autoSizeColumns : false,
32328
32329     /**
32330      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32331      */
32332     autoSizeHeaders : true,
32333
32334     /**
32335      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32336      */
32337     monitorWindowResize : true,
32338
32339     /**
32340      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32341      * rows measured to get a columns size. Default is 0 (all rows).
32342      */
32343     maxRowsToMeasure : 0,
32344
32345     /**
32346      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32347      */
32348     trackMouseOver : true,
32349
32350     /**
32351     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32352     */
32353     
32354     /**
32355     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32356     */
32357     enableDragDrop : false,
32358     
32359     /**
32360     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32361     */
32362     enableColumnMove : true,
32363     
32364     /**
32365     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32366     */
32367     enableColumnHide : true,
32368     
32369     /**
32370     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32371     */
32372     enableRowHeightSync : false,
32373     
32374     /**
32375     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32376     */
32377     stripeRows : true,
32378     
32379     /**
32380     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32381     */
32382     autoHeight : false,
32383
32384     /**
32385      * @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.
32386      */
32387     autoExpandColumn : false,
32388
32389     /**
32390     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32391     * Default is 50.
32392     */
32393     autoExpandMin : 50,
32394
32395     /**
32396     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32397     */
32398     autoExpandMax : 1000,
32399
32400     /**
32401     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32402     */
32403     view : null,
32404
32405     /**
32406     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32407     */
32408     loadMask : false,
32409     /**
32410     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32411     */
32412     dropTarget: false,
32413     
32414    
32415     
32416     // private
32417     rendered : false,
32418
32419     /**
32420     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32421     * of a fixed width. Default is false.
32422     */
32423     /**
32424     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32425     */
32426     /**
32427      * Called once after all setup has been completed and the grid is ready to be rendered.
32428      * @return {Roo.grid.Grid} this
32429      */
32430     render : function()
32431     {
32432         var c = this.container;
32433         // try to detect autoHeight/width mode
32434         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32435             this.autoHeight = true;
32436         }
32437         var view = this.getView();
32438         view.init(this);
32439
32440         c.on("click", this.onClick, this);
32441         c.on("dblclick", this.onDblClick, this);
32442         c.on("contextmenu", this.onContextMenu, this);
32443         c.on("keydown", this.onKeyDown, this);
32444         if (Roo.isTouch) {
32445             c.on("touchstart", this.onTouchStart, this);
32446         }
32447
32448         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32449
32450         this.getSelectionModel().init(this);
32451
32452         view.render();
32453
32454         if(this.loadMask){
32455             this.loadMask = new Roo.LoadMask(this.container,
32456                     Roo.apply({store:this.dataSource}, this.loadMask));
32457         }
32458         
32459         
32460         if (this.toolbar && this.toolbar.xtype) {
32461             this.toolbar.container = this.getView().getHeaderPanel(true);
32462             this.toolbar = new Roo.Toolbar(this.toolbar);
32463         }
32464         if (this.footer && this.footer.xtype) {
32465             this.footer.dataSource = this.getDataSource();
32466             this.footer.container = this.getView().getFooterPanel(true);
32467             this.footer = Roo.factory(this.footer, Roo);
32468         }
32469         if (this.dropTarget && this.dropTarget.xtype) {
32470             delete this.dropTarget.xtype;
32471             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32472         }
32473         
32474         
32475         this.rendered = true;
32476         this.fireEvent('render', this);
32477         return this;
32478     },
32479
32480         /**
32481          * Reconfigures the grid to use a different Store and Column Model.
32482          * The View will be bound to the new objects and refreshed.
32483          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32484          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32485          */
32486     reconfigure : function(dataSource, colModel){
32487         if(this.loadMask){
32488             this.loadMask.destroy();
32489             this.loadMask = new Roo.LoadMask(this.container,
32490                     Roo.apply({store:dataSource}, this.loadMask));
32491         }
32492         this.view.bind(dataSource, colModel);
32493         this.dataSource = dataSource;
32494         this.colModel = colModel;
32495         this.view.refresh(true);
32496     },
32497
32498     // private
32499     onKeyDown : function(e){
32500         this.fireEvent("keydown", e);
32501     },
32502
32503     /**
32504      * Destroy this grid.
32505      * @param {Boolean} removeEl True to remove the element
32506      */
32507     destroy : function(removeEl, keepListeners){
32508         if(this.loadMask){
32509             this.loadMask.destroy();
32510         }
32511         var c = this.container;
32512         c.removeAllListeners();
32513         this.view.destroy();
32514         this.colModel.purgeListeners();
32515         if(!keepListeners){
32516             this.purgeListeners();
32517         }
32518         c.update("");
32519         if(removeEl === true){
32520             c.remove();
32521         }
32522     },
32523
32524     // private
32525     processEvent : function(name, e){
32526         // does this fire select???
32527         //Roo.log('grid:processEvent '  + name);
32528         
32529         if (name != 'touchstart' ) {
32530             this.fireEvent(name, e);    
32531         }
32532         
32533         var t = e.getTarget();
32534         var v = this.view;
32535         var header = v.findHeaderIndex(t);
32536         if(header !== false){
32537             var ename = name == 'touchstart' ? 'click' : name;
32538              
32539             this.fireEvent("header" + ename, this, header, e);
32540         }else{
32541             var row = v.findRowIndex(t);
32542             var cell = v.findCellIndex(t);
32543             if (name == 'touchstart') {
32544                 // first touch is always a click.
32545                 // hopefull this happens after selection is updated.?
32546                 name = false;
32547                 
32548                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32549                     var cs = this.selModel.getSelectedCell();
32550                     if (row == cs[0] && cell == cs[1]){
32551                         name = 'dblclick';
32552                     }
32553                 }
32554                 if (typeof(this.selModel.getSelections) != 'undefined') {
32555                     var cs = this.selModel.getSelections();
32556                     var ds = this.dataSource;
32557                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32558                         name = 'dblclick';
32559                     }
32560                 }
32561                 if (!name) {
32562                     return;
32563                 }
32564             }
32565             
32566             
32567             if(row !== false){
32568                 this.fireEvent("row" + name, this, row, e);
32569                 if(cell !== false){
32570                     this.fireEvent("cell" + name, this, row, cell, e);
32571                 }
32572             }
32573         }
32574     },
32575
32576     // private
32577     onClick : function(e){
32578         this.processEvent("click", e);
32579     },
32580    // private
32581     onTouchStart : function(e){
32582         this.processEvent("touchstart", e);
32583     },
32584
32585     // private
32586     onContextMenu : function(e, t){
32587         this.processEvent("contextmenu", e);
32588     },
32589
32590     // private
32591     onDblClick : function(e){
32592         this.processEvent("dblclick", e);
32593     },
32594
32595     // private
32596     walkCells : function(row, col, step, fn, scope){
32597         var cm = this.colModel, clen = cm.getColumnCount();
32598         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32599         if(step < 0){
32600             if(col < 0){
32601                 row--;
32602                 first = false;
32603             }
32604             while(row >= 0){
32605                 if(!first){
32606                     col = clen-1;
32607                 }
32608                 first = false;
32609                 while(col >= 0){
32610                     if(fn.call(scope || this, row, col, cm) === true){
32611                         return [row, col];
32612                     }
32613                     col--;
32614                 }
32615                 row--;
32616             }
32617         } else {
32618             if(col >= clen){
32619                 row++;
32620                 first = false;
32621             }
32622             while(row < rlen){
32623                 if(!first){
32624                     col = 0;
32625                 }
32626                 first = false;
32627                 while(col < clen){
32628                     if(fn.call(scope || this, row, col, cm) === true){
32629                         return [row, col];
32630                     }
32631                     col++;
32632                 }
32633                 row++;
32634             }
32635         }
32636         return null;
32637     },
32638
32639     // private
32640     getSelections : function(){
32641         return this.selModel.getSelections();
32642     },
32643
32644     /**
32645      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32646      * but if manual update is required this method will initiate it.
32647      */
32648     autoSize : function(){
32649         if(this.rendered){
32650             this.view.layout();
32651             if(this.view.adjustForScroll){
32652                 this.view.adjustForScroll();
32653             }
32654         }
32655     },
32656
32657     /**
32658      * Returns the grid's underlying element.
32659      * @return {Element} The element
32660      */
32661     getGridEl : function(){
32662         return this.container;
32663     },
32664
32665     // private for compatibility, overridden by editor grid
32666     stopEditing : function(){},
32667
32668     /**
32669      * Returns the grid's SelectionModel.
32670      * @return {SelectionModel}
32671      */
32672     getSelectionModel : function(){
32673         if(!this.selModel){
32674             this.selModel = new Roo.grid.RowSelectionModel();
32675         }
32676         return this.selModel;
32677     },
32678
32679     /**
32680      * Returns the grid's DataSource.
32681      * @return {DataSource}
32682      */
32683     getDataSource : function(){
32684         return this.dataSource;
32685     },
32686
32687     /**
32688      * Returns the grid's ColumnModel.
32689      * @return {ColumnModel}
32690      */
32691     getColumnModel : function(){
32692         return this.colModel;
32693     },
32694
32695     /**
32696      * Returns the grid's GridView object.
32697      * @return {GridView}
32698      */
32699     getView : function(){
32700         if(!this.view){
32701             this.view = new Roo.grid.GridView(this.viewConfig);
32702         }
32703         return this.view;
32704     },
32705     /**
32706      * Called to get grid's drag proxy text, by default returns this.ddText.
32707      * @return {String}
32708      */
32709     getDragDropText : function(){
32710         var count = this.selModel.getCount();
32711         return String.format(this.ddText, count, count == 1 ? '' : 's');
32712     }
32713 });
32714 /**
32715  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32716  * %0 is replaced with the number of selected rows.
32717  * @type String
32718  */
32719 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32720  * Based on:
32721  * Ext JS Library 1.1.1
32722  * Copyright(c) 2006-2007, Ext JS, LLC.
32723  *
32724  * Originally Released Under LGPL - original licence link has changed is not relivant.
32725  *
32726  * Fork - LGPL
32727  * <script type="text/javascript">
32728  */
32729  
32730 Roo.grid.AbstractGridView = function(){
32731         this.grid = null;
32732         
32733         this.events = {
32734             "beforerowremoved" : true,
32735             "beforerowsinserted" : true,
32736             "beforerefresh" : true,
32737             "rowremoved" : true,
32738             "rowsinserted" : true,
32739             "rowupdated" : true,
32740             "refresh" : true
32741         };
32742     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32743 };
32744
32745 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32746     rowClass : "x-grid-row",
32747     cellClass : "x-grid-cell",
32748     tdClass : "x-grid-td",
32749     hdClass : "x-grid-hd",
32750     splitClass : "x-grid-hd-split",
32751     
32752     init: function(grid){
32753         this.grid = grid;
32754                 var cid = this.grid.getGridEl().id;
32755         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32756         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32757         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32758         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32759         },
32760         
32761     getColumnRenderers : function(){
32762         var renderers = [];
32763         var cm = this.grid.colModel;
32764         var colCount = cm.getColumnCount();
32765         for(var i = 0; i < colCount; i++){
32766             renderers[i] = cm.getRenderer(i);
32767         }
32768         return renderers;
32769     },
32770     
32771     getColumnIds : function(){
32772         var ids = [];
32773         var cm = this.grid.colModel;
32774         var colCount = cm.getColumnCount();
32775         for(var i = 0; i < colCount; i++){
32776             ids[i] = cm.getColumnId(i);
32777         }
32778         return ids;
32779     },
32780     
32781     getDataIndexes : function(){
32782         if(!this.indexMap){
32783             this.indexMap = this.buildIndexMap();
32784         }
32785         return this.indexMap.colToData;
32786     },
32787     
32788     getColumnIndexByDataIndex : function(dataIndex){
32789         if(!this.indexMap){
32790             this.indexMap = this.buildIndexMap();
32791         }
32792         return this.indexMap.dataToCol[dataIndex];
32793     },
32794     
32795     /**
32796      * Set a css style for a column dynamically. 
32797      * @param {Number} colIndex The index of the column
32798      * @param {String} name The css property name
32799      * @param {String} value The css value
32800      */
32801     setCSSStyle : function(colIndex, name, value){
32802         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32803         Roo.util.CSS.updateRule(selector, name, value);
32804     },
32805     
32806     generateRules : function(cm){
32807         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32808         Roo.util.CSS.removeStyleSheet(rulesId);
32809         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32810             var cid = cm.getColumnId(i);
32811             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32812                          this.tdSelector, cid, " {\n}\n",
32813                          this.hdSelector, cid, " {\n}\n",
32814                          this.splitSelector, cid, " {\n}\n");
32815         }
32816         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32817     }
32818 });/*
32819  * Based on:
32820  * Ext JS Library 1.1.1
32821  * Copyright(c) 2006-2007, Ext JS, LLC.
32822  *
32823  * Originally Released Under LGPL - original licence link has changed is not relivant.
32824  *
32825  * Fork - LGPL
32826  * <script type="text/javascript">
32827  */
32828
32829 // private
32830 // This is a support class used internally by the Grid components
32831 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32832     this.grid = grid;
32833     this.view = grid.getView();
32834     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32835     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32836     if(hd2){
32837         this.setHandleElId(Roo.id(hd));
32838         this.setOuterHandleElId(Roo.id(hd2));
32839     }
32840     this.scroll = false;
32841 };
32842 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32843     maxDragWidth: 120,
32844     getDragData : function(e){
32845         var t = Roo.lib.Event.getTarget(e);
32846         var h = this.view.findHeaderCell(t);
32847         if(h){
32848             return {ddel: h.firstChild, header:h};
32849         }
32850         return false;
32851     },
32852
32853     onInitDrag : function(e){
32854         this.view.headersDisabled = true;
32855         var clone = this.dragData.ddel.cloneNode(true);
32856         clone.id = Roo.id();
32857         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32858         this.proxy.update(clone);
32859         return true;
32860     },
32861
32862     afterValidDrop : function(){
32863         var v = this.view;
32864         setTimeout(function(){
32865             v.headersDisabled = false;
32866         }, 50);
32867     },
32868
32869     afterInvalidDrop : function(){
32870         var v = this.view;
32871         setTimeout(function(){
32872             v.headersDisabled = false;
32873         }, 50);
32874     }
32875 });
32876 /*
32877  * Based on:
32878  * Ext JS Library 1.1.1
32879  * Copyright(c) 2006-2007, Ext JS, LLC.
32880  *
32881  * Originally Released Under LGPL - original licence link has changed is not relivant.
32882  *
32883  * Fork - LGPL
32884  * <script type="text/javascript">
32885  */
32886 // private
32887 // This is a support class used internally by the Grid components
32888 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32889     this.grid = grid;
32890     this.view = grid.getView();
32891     // split the proxies so they don't interfere with mouse events
32892     this.proxyTop = Roo.DomHelper.append(document.body, {
32893         cls:"col-move-top", html:"&#160;"
32894     }, true);
32895     this.proxyBottom = Roo.DomHelper.append(document.body, {
32896         cls:"col-move-bottom", html:"&#160;"
32897     }, true);
32898     this.proxyTop.hide = this.proxyBottom.hide = function(){
32899         this.setLeftTop(-100,-100);
32900         this.setStyle("visibility", "hidden");
32901     };
32902     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32903     // temporarily disabled
32904     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32905     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32906 };
32907 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32908     proxyOffsets : [-4, -9],
32909     fly: Roo.Element.fly,
32910
32911     getTargetFromEvent : function(e){
32912         var t = Roo.lib.Event.getTarget(e);
32913         var cindex = this.view.findCellIndex(t);
32914         if(cindex !== false){
32915             return this.view.getHeaderCell(cindex);
32916         }
32917         return null;
32918     },
32919
32920     nextVisible : function(h){
32921         var v = this.view, cm = this.grid.colModel;
32922         h = h.nextSibling;
32923         while(h){
32924             if(!cm.isHidden(v.getCellIndex(h))){
32925                 return h;
32926             }
32927             h = h.nextSibling;
32928         }
32929         return null;
32930     },
32931
32932     prevVisible : function(h){
32933         var v = this.view, cm = this.grid.colModel;
32934         h = h.prevSibling;
32935         while(h){
32936             if(!cm.isHidden(v.getCellIndex(h))){
32937                 return h;
32938             }
32939             h = h.prevSibling;
32940         }
32941         return null;
32942     },
32943
32944     positionIndicator : function(h, n, e){
32945         var x = Roo.lib.Event.getPageX(e);
32946         var r = Roo.lib.Dom.getRegion(n.firstChild);
32947         var px, pt, py = r.top + this.proxyOffsets[1];
32948         if((r.right - x) <= (r.right-r.left)/2){
32949             px = r.right+this.view.borderWidth;
32950             pt = "after";
32951         }else{
32952             px = r.left;
32953             pt = "before";
32954         }
32955         var oldIndex = this.view.getCellIndex(h);
32956         var newIndex = this.view.getCellIndex(n);
32957
32958         if(this.grid.colModel.isFixed(newIndex)){
32959             return false;
32960         }
32961
32962         var locked = this.grid.colModel.isLocked(newIndex);
32963
32964         if(pt == "after"){
32965             newIndex++;
32966         }
32967         if(oldIndex < newIndex){
32968             newIndex--;
32969         }
32970         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32971             return false;
32972         }
32973         px +=  this.proxyOffsets[0];
32974         this.proxyTop.setLeftTop(px, py);
32975         this.proxyTop.show();
32976         if(!this.bottomOffset){
32977             this.bottomOffset = this.view.mainHd.getHeight();
32978         }
32979         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32980         this.proxyBottom.show();
32981         return pt;
32982     },
32983
32984     onNodeEnter : function(n, dd, e, data){
32985         if(data.header != n){
32986             this.positionIndicator(data.header, n, e);
32987         }
32988     },
32989
32990     onNodeOver : function(n, dd, e, data){
32991         var result = false;
32992         if(data.header != n){
32993             result = this.positionIndicator(data.header, n, e);
32994         }
32995         if(!result){
32996             this.proxyTop.hide();
32997             this.proxyBottom.hide();
32998         }
32999         return result ? this.dropAllowed : this.dropNotAllowed;
33000     },
33001
33002     onNodeOut : function(n, dd, e, data){
33003         this.proxyTop.hide();
33004         this.proxyBottom.hide();
33005     },
33006
33007     onNodeDrop : function(n, dd, e, data){
33008         var h = data.header;
33009         if(h != n){
33010             var cm = this.grid.colModel;
33011             var x = Roo.lib.Event.getPageX(e);
33012             var r = Roo.lib.Dom.getRegion(n.firstChild);
33013             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33014             var oldIndex = this.view.getCellIndex(h);
33015             var newIndex = this.view.getCellIndex(n);
33016             var locked = cm.isLocked(newIndex);
33017             if(pt == "after"){
33018                 newIndex++;
33019             }
33020             if(oldIndex < newIndex){
33021                 newIndex--;
33022             }
33023             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33024                 return false;
33025             }
33026             cm.setLocked(oldIndex, locked, true);
33027             cm.moveColumn(oldIndex, newIndex);
33028             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33029             return true;
33030         }
33031         return false;
33032     }
33033 });
33034 /*
33035  * Based on:
33036  * Ext JS Library 1.1.1
33037  * Copyright(c) 2006-2007, Ext JS, LLC.
33038  *
33039  * Originally Released Under LGPL - original licence link has changed is not relivant.
33040  *
33041  * Fork - LGPL
33042  * <script type="text/javascript">
33043  */
33044   
33045 /**
33046  * @class Roo.grid.GridView
33047  * @extends Roo.util.Observable
33048  *
33049  * @constructor
33050  * @param {Object} config
33051  */
33052 Roo.grid.GridView = function(config){
33053     Roo.grid.GridView.superclass.constructor.call(this);
33054     this.el = null;
33055
33056     Roo.apply(this, config);
33057 };
33058
33059 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33060
33061     unselectable :  'unselectable="on"',
33062     unselectableCls :  'x-unselectable',
33063     
33064     
33065     rowClass : "x-grid-row",
33066
33067     cellClass : "x-grid-col",
33068
33069     tdClass : "x-grid-td",
33070
33071     hdClass : "x-grid-hd",
33072
33073     splitClass : "x-grid-split",
33074
33075     sortClasses : ["sort-asc", "sort-desc"],
33076
33077     enableMoveAnim : false,
33078
33079     hlColor: "C3DAF9",
33080
33081     dh : Roo.DomHelper,
33082
33083     fly : Roo.Element.fly,
33084
33085     css : Roo.util.CSS,
33086
33087     borderWidth: 1,
33088
33089     splitOffset: 3,
33090
33091     scrollIncrement : 22,
33092
33093     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33094
33095     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33096
33097     bind : function(ds, cm){
33098         if(this.ds){
33099             this.ds.un("load", this.onLoad, this);
33100             this.ds.un("datachanged", this.onDataChange, this);
33101             this.ds.un("add", this.onAdd, this);
33102             this.ds.un("remove", this.onRemove, this);
33103             this.ds.un("update", this.onUpdate, this);
33104             this.ds.un("clear", this.onClear, this);
33105         }
33106         if(ds){
33107             ds.on("load", this.onLoad, this);
33108             ds.on("datachanged", this.onDataChange, this);
33109             ds.on("add", this.onAdd, this);
33110             ds.on("remove", this.onRemove, this);
33111             ds.on("update", this.onUpdate, this);
33112             ds.on("clear", this.onClear, this);
33113         }
33114         this.ds = ds;
33115
33116         if(this.cm){
33117             this.cm.un("widthchange", this.onColWidthChange, this);
33118             this.cm.un("headerchange", this.onHeaderChange, this);
33119             this.cm.un("hiddenchange", this.onHiddenChange, this);
33120             this.cm.un("columnmoved", this.onColumnMove, this);
33121             this.cm.un("columnlockchange", this.onColumnLock, this);
33122         }
33123         if(cm){
33124             this.generateRules(cm);
33125             cm.on("widthchange", this.onColWidthChange, this);
33126             cm.on("headerchange", this.onHeaderChange, this);
33127             cm.on("hiddenchange", this.onHiddenChange, this);
33128             cm.on("columnmoved", this.onColumnMove, this);
33129             cm.on("columnlockchange", this.onColumnLock, this);
33130         }
33131         this.cm = cm;
33132     },
33133
33134     init: function(grid){
33135         Roo.grid.GridView.superclass.init.call(this, grid);
33136
33137         this.bind(grid.dataSource, grid.colModel);
33138
33139         grid.on("headerclick", this.handleHeaderClick, this);
33140
33141         if(grid.trackMouseOver){
33142             grid.on("mouseover", this.onRowOver, this);
33143             grid.on("mouseout", this.onRowOut, this);
33144         }
33145         grid.cancelTextSelection = function(){};
33146         this.gridId = grid.id;
33147
33148         var tpls = this.templates || {};
33149
33150         if(!tpls.master){
33151             tpls.master = new Roo.Template(
33152                '<div class="x-grid" hidefocus="true">',
33153                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33154                   '<div class="x-grid-topbar"></div>',
33155                   '<div class="x-grid-scroller"><div></div></div>',
33156                   '<div class="x-grid-locked">',
33157                       '<div class="x-grid-header">{lockedHeader}</div>',
33158                       '<div class="x-grid-body">{lockedBody}</div>',
33159                   "</div>",
33160                   '<div class="x-grid-viewport">',
33161                       '<div class="x-grid-header">{header}</div>',
33162                       '<div class="x-grid-body">{body}</div>',
33163                   "</div>",
33164                   '<div class="x-grid-bottombar"></div>',
33165                  
33166                   '<div class="x-grid-resize-proxy">&#160;</div>',
33167                "</div>"
33168             );
33169             tpls.master.disableformats = true;
33170         }
33171
33172         if(!tpls.header){
33173             tpls.header = new Roo.Template(
33174                '<table border="0" cellspacing="0" cellpadding="0">',
33175                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33176                "</table>{splits}"
33177             );
33178             tpls.header.disableformats = true;
33179         }
33180         tpls.header.compile();
33181
33182         if(!tpls.hcell){
33183             tpls.hcell = new Roo.Template(
33184                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33185                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33186                 "</div></td>"
33187              );
33188              tpls.hcell.disableFormats = true;
33189         }
33190         tpls.hcell.compile();
33191
33192         if(!tpls.hsplit){
33193             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33194                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33195             tpls.hsplit.disableFormats = true;
33196         }
33197         tpls.hsplit.compile();
33198
33199         if(!tpls.body){
33200             tpls.body = new Roo.Template(
33201                '<table border="0" cellspacing="0" cellpadding="0">',
33202                "<tbody>{rows}</tbody>",
33203                "</table>"
33204             );
33205             tpls.body.disableFormats = true;
33206         }
33207         tpls.body.compile();
33208
33209         if(!tpls.row){
33210             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33211             tpls.row.disableFormats = true;
33212         }
33213         tpls.row.compile();
33214
33215         if(!tpls.cell){
33216             tpls.cell = new Roo.Template(
33217                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33218                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33219                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33220                 "</td>"
33221             );
33222             tpls.cell.disableFormats = true;
33223         }
33224         tpls.cell.compile();
33225
33226         this.templates = tpls;
33227     },
33228
33229     // remap these for backwards compat
33230     onColWidthChange : function(){
33231         this.updateColumns.apply(this, arguments);
33232     },
33233     onHeaderChange : function(){
33234         this.updateHeaders.apply(this, arguments);
33235     }, 
33236     onHiddenChange : function(){
33237         this.handleHiddenChange.apply(this, arguments);
33238     },
33239     onColumnMove : function(){
33240         this.handleColumnMove.apply(this, arguments);
33241     },
33242     onColumnLock : function(){
33243         this.handleLockChange.apply(this, arguments);
33244     },
33245
33246     onDataChange : function(){
33247         this.refresh();
33248         this.updateHeaderSortState();
33249     },
33250
33251     onClear : function(){
33252         this.refresh();
33253     },
33254
33255     onUpdate : function(ds, record){
33256         this.refreshRow(record);
33257     },
33258
33259     refreshRow : function(record){
33260         var ds = this.ds, index;
33261         if(typeof record == 'number'){
33262             index = record;
33263             record = ds.getAt(index);
33264         }else{
33265             index = ds.indexOf(record);
33266         }
33267         this.insertRows(ds, index, index, true);
33268         this.onRemove(ds, record, index+1, true);
33269         this.syncRowHeights(index, index);
33270         this.layout();
33271         this.fireEvent("rowupdated", this, index, record);
33272     },
33273
33274     onAdd : function(ds, records, index){
33275         this.insertRows(ds, index, index + (records.length-1));
33276     },
33277
33278     onRemove : function(ds, record, index, isUpdate){
33279         if(isUpdate !== true){
33280             this.fireEvent("beforerowremoved", this, index, record);
33281         }
33282         var bt = this.getBodyTable(), lt = this.getLockedTable();
33283         if(bt.rows[index]){
33284             bt.firstChild.removeChild(bt.rows[index]);
33285         }
33286         if(lt.rows[index]){
33287             lt.firstChild.removeChild(lt.rows[index]);
33288         }
33289         if(isUpdate !== true){
33290             this.stripeRows(index);
33291             this.syncRowHeights(index, index);
33292             this.layout();
33293             this.fireEvent("rowremoved", this, index, record);
33294         }
33295     },
33296
33297     onLoad : function(){
33298         this.scrollToTop();
33299     },
33300
33301     /**
33302      * Scrolls the grid to the top
33303      */
33304     scrollToTop : function(){
33305         if(this.scroller){
33306             this.scroller.dom.scrollTop = 0;
33307             this.syncScroll();
33308         }
33309     },
33310
33311     /**
33312      * Gets a panel in the header of the grid that can be used for toolbars etc.
33313      * After modifying the contents of this panel a call to grid.autoSize() may be
33314      * required to register any changes in size.
33315      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33316      * @return Roo.Element
33317      */
33318     getHeaderPanel : function(doShow){
33319         if(doShow){
33320             this.headerPanel.show();
33321         }
33322         return this.headerPanel;
33323     },
33324
33325     /**
33326      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33327      * After modifying the contents of this panel a call to grid.autoSize() may be
33328      * required to register any changes in size.
33329      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33330      * @return Roo.Element
33331      */
33332     getFooterPanel : function(doShow){
33333         if(doShow){
33334             this.footerPanel.show();
33335         }
33336         return this.footerPanel;
33337     },
33338
33339     initElements : function(){
33340         var E = Roo.Element;
33341         var el = this.grid.getGridEl().dom.firstChild;
33342         var cs = el.childNodes;
33343
33344         this.el = new E(el);
33345         
33346          this.focusEl = new E(el.firstChild);
33347         this.focusEl.swallowEvent("click", true);
33348         
33349         this.headerPanel = new E(cs[1]);
33350         this.headerPanel.enableDisplayMode("block");
33351
33352         this.scroller = new E(cs[2]);
33353         this.scrollSizer = new E(this.scroller.dom.firstChild);
33354
33355         this.lockedWrap = new E(cs[3]);
33356         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33357         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33358
33359         this.mainWrap = new E(cs[4]);
33360         this.mainHd = new E(this.mainWrap.dom.firstChild);
33361         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33362
33363         this.footerPanel = new E(cs[5]);
33364         this.footerPanel.enableDisplayMode("block");
33365
33366         this.resizeProxy = new E(cs[6]);
33367
33368         this.headerSelector = String.format(
33369            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33370            this.lockedHd.id, this.mainHd.id
33371         );
33372
33373         this.splitterSelector = String.format(
33374            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33375            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33376         );
33377     },
33378     idToCssName : function(s)
33379     {
33380         return s.replace(/[^a-z0-9]+/ig, '-');
33381     },
33382
33383     getHeaderCell : function(index){
33384         return Roo.DomQuery.select(this.headerSelector)[index];
33385     },
33386
33387     getHeaderCellMeasure : function(index){
33388         return this.getHeaderCell(index).firstChild;
33389     },
33390
33391     getHeaderCellText : function(index){
33392         return this.getHeaderCell(index).firstChild.firstChild;
33393     },
33394
33395     getLockedTable : function(){
33396         return this.lockedBody.dom.firstChild;
33397     },
33398
33399     getBodyTable : function(){
33400         return this.mainBody.dom.firstChild;
33401     },
33402
33403     getLockedRow : function(index){
33404         return this.getLockedTable().rows[index];
33405     },
33406
33407     getRow : function(index){
33408         return this.getBodyTable().rows[index];
33409     },
33410
33411     getRowComposite : function(index){
33412         if(!this.rowEl){
33413             this.rowEl = new Roo.CompositeElementLite();
33414         }
33415         var els = [], lrow, mrow;
33416         if(lrow = this.getLockedRow(index)){
33417             els.push(lrow);
33418         }
33419         if(mrow = this.getRow(index)){
33420             els.push(mrow);
33421         }
33422         this.rowEl.elements = els;
33423         return this.rowEl;
33424     },
33425     /**
33426      * Gets the 'td' of the cell
33427      * 
33428      * @param {Integer} rowIndex row to select
33429      * @param {Integer} colIndex column to select
33430      * 
33431      * @return {Object} 
33432      */
33433     getCell : function(rowIndex, colIndex){
33434         var locked = this.cm.getLockedCount();
33435         var source;
33436         if(colIndex < locked){
33437             source = this.lockedBody.dom.firstChild;
33438         }else{
33439             source = this.mainBody.dom.firstChild;
33440             colIndex -= locked;
33441         }
33442         return source.rows[rowIndex].childNodes[colIndex];
33443     },
33444
33445     getCellText : function(rowIndex, colIndex){
33446         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33447     },
33448
33449     getCellBox : function(cell){
33450         var b = this.fly(cell).getBox();
33451         if(Roo.isOpera){ // opera fails to report the Y
33452             b.y = cell.offsetTop + this.mainBody.getY();
33453         }
33454         return b;
33455     },
33456
33457     getCellIndex : function(cell){
33458         var id = String(cell.className).match(this.cellRE);
33459         if(id){
33460             return parseInt(id[1], 10);
33461         }
33462         return 0;
33463     },
33464
33465     findHeaderIndex : function(n){
33466         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33467         return r ? this.getCellIndex(r) : false;
33468     },
33469
33470     findHeaderCell : function(n){
33471         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33472         return r ? r : false;
33473     },
33474
33475     findRowIndex : function(n){
33476         if(!n){
33477             return false;
33478         }
33479         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33480         return r ? r.rowIndex : false;
33481     },
33482
33483     findCellIndex : function(node){
33484         var stop = this.el.dom;
33485         while(node && node != stop){
33486             if(this.findRE.test(node.className)){
33487                 return this.getCellIndex(node);
33488             }
33489             node = node.parentNode;
33490         }
33491         return false;
33492     },
33493
33494     getColumnId : function(index){
33495         return this.cm.getColumnId(index);
33496     },
33497
33498     getSplitters : function()
33499     {
33500         if(this.splitterSelector){
33501            return Roo.DomQuery.select(this.splitterSelector);
33502         }else{
33503             return null;
33504       }
33505     },
33506
33507     getSplitter : function(index){
33508         return this.getSplitters()[index];
33509     },
33510
33511     onRowOver : function(e, t){
33512         var row;
33513         if((row = this.findRowIndex(t)) !== false){
33514             this.getRowComposite(row).addClass("x-grid-row-over");
33515         }
33516     },
33517
33518     onRowOut : function(e, t){
33519         var row;
33520         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33521             this.getRowComposite(row).removeClass("x-grid-row-over");
33522         }
33523     },
33524
33525     renderHeaders : function(){
33526         var cm = this.cm;
33527         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33528         var cb = [], lb = [], sb = [], lsb = [], p = {};
33529         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33530             p.cellId = "x-grid-hd-0-" + i;
33531             p.splitId = "x-grid-csplit-0-" + i;
33532             p.id = cm.getColumnId(i);
33533             p.value = cm.getColumnHeader(i) || "";
33534             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33535             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33536             if(!cm.isLocked(i)){
33537                 cb[cb.length] = ct.apply(p);
33538                 sb[sb.length] = st.apply(p);
33539             }else{
33540                 lb[lb.length] = ct.apply(p);
33541                 lsb[lsb.length] = st.apply(p);
33542             }
33543         }
33544         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33545                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33546     },
33547
33548     updateHeaders : function(){
33549         var html = this.renderHeaders();
33550         this.lockedHd.update(html[0]);
33551         this.mainHd.update(html[1]);
33552     },
33553
33554     /**
33555      * Focuses the specified row.
33556      * @param {Number} row The row index
33557      */
33558     focusRow : function(row)
33559     {
33560         //Roo.log('GridView.focusRow');
33561         var x = this.scroller.dom.scrollLeft;
33562         this.focusCell(row, 0, false);
33563         this.scroller.dom.scrollLeft = x;
33564     },
33565
33566     /**
33567      * Focuses the specified cell.
33568      * @param {Number} row The row index
33569      * @param {Number} col The column index
33570      * @param {Boolean} hscroll false to disable horizontal scrolling
33571      */
33572     focusCell : function(row, col, hscroll)
33573     {
33574         //Roo.log('GridView.focusCell');
33575         var el = this.ensureVisible(row, col, hscroll);
33576         this.focusEl.alignTo(el, "tl-tl");
33577         if(Roo.isGecko){
33578             this.focusEl.focus();
33579         }else{
33580             this.focusEl.focus.defer(1, this.focusEl);
33581         }
33582     },
33583
33584     /**
33585      * Scrolls the specified cell into view
33586      * @param {Number} row The row index
33587      * @param {Number} col The column index
33588      * @param {Boolean} hscroll false to disable horizontal scrolling
33589      */
33590     ensureVisible : function(row, col, hscroll)
33591     {
33592         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33593         //return null; //disable for testing.
33594         if(typeof row != "number"){
33595             row = row.rowIndex;
33596         }
33597         if(row < 0 && row >= this.ds.getCount()){
33598             return  null;
33599         }
33600         col = (col !== undefined ? col : 0);
33601         var cm = this.grid.colModel;
33602         while(cm.isHidden(col)){
33603             col++;
33604         }
33605
33606         var el = this.getCell(row, col);
33607         if(!el){
33608             return null;
33609         }
33610         var c = this.scroller.dom;
33611
33612         var ctop = parseInt(el.offsetTop, 10);
33613         var cleft = parseInt(el.offsetLeft, 10);
33614         var cbot = ctop + el.offsetHeight;
33615         var cright = cleft + el.offsetWidth;
33616         
33617         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33618         var stop = parseInt(c.scrollTop, 10);
33619         var sleft = parseInt(c.scrollLeft, 10);
33620         var sbot = stop + ch;
33621         var sright = sleft + c.clientWidth;
33622         /*
33623         Roo.log('GridView.ensureVisible:' +
33624                 ' ctop:' + ctop +
33625                 ' c.clientHeight:' + c.clientHeight +
33626                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33627                 ' stop:' + stop +
33628                 ' cbot:' + cbot +
33629                 ' sbot:' + sbot +
33630                 ' ch:' + ch  
33631                 );
33632         */
33633         if(ctop < stop){
33634              c.scrollTop = ctop;
33635             //Roo.log("set scrolltop to ctop DISABLE?");
33636         }else if(cbot > sbot){
33637             //Roo.log("set scrolltop to cbot-ch");
33638             c.scrollTop = cbot-ch;
33639         }
33640         
33641         if(hscroll !== false){
33642             if(cleft < sleft){
33643                 c.scrollLeft = cleft;
33644             }else if(cright > sright){
33645                 c.scrollLeft = cright-c.clientWidth;
33646             }
33647         }
33648          
33649         return el;
33650     },
33651
33652     updateColumns : function(){
33653         this.grid.stopEditing();
33654         var cm = this.grid.colModel, colIds = this.getColumnIds();
33655         //var totalWidth = cm.getTotalWidth();
33656         var pos = 0;
33657         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33658             //if(cm.isHidden(i)) continue;
33659             var w = cm.getColumnWidth(i);
33660             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33661             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33662         }
33663         this.updateSplitters();
33664     },
33665
33666     generateRules : function(cm){
33667         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33668         Roo.util.CSS.removeStyleSheet(rulesId);
33669         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33670             var cid = cm.getColumnId(i);
33671             var align = '';
33672             if(cm.config[i].align){
33673                 align = 'text-align:'+cm.config[i].align+';';
33674             }
33675             var hidden = '';
33676             if(cm.isHidden(i)){
33677                 hidden = 'display:none;';
33678             }
33679             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33680             ruleBuf.push(
33681                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33682                     this.hdSelector, cid, " {\n", align, width, "}\n",
33683                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33684                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33685         }
33686         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33687     },
33688
33689     updateSplitters : function(){
33690         var cm = this.cm, s = this.getSplitters();
33691         if(s){ // splitters not created yet
33692             var pos = 0, locked = true;
33693             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33694                 if(cm.isHidden(i)) {
33695                     continue;
33696                 }
33697                 var w = cm.getColumnWidth(i); // make sure it's a number
33698                 if(!cm.isLocked(i) && locked){
33699                     pos = 0;
33700                     locked = false;
33701                 }
33702                 pos += w;
33703                 s[i].style.left = (pos-this.splitOffset) + "px";
33704             }
33705         }
33706     },
33707
33708     handleHiddenChange : function(colModel, colIndex, hidden){
33709         if(hidden){
33710             this.hideColumn(colIndex);
33711         }else{
33712             this.unhideColumn(colIndex);
33713         }
33714     },
33715
33716     hideColumn : function(colIndex){
33717         var cid = this.getColumnId(colIndex);
33718         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33719         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33720         if(Roo.isSafari){
33721             this.updateHeaders();
33722         }
33723         this.updateSplitters();
33724         this.layout();
33725     },
33726
33727     unhideColumn : function(colIndex){
33728         var cid = this.getColumnId(colIndex);
33729         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33730         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33731
33732         if(Roo.isSafari){
33733             this.updateHeaders();
33734         }
33735         this.updateSplitters();
33736         this.layout();
33737     },
33738
33739     insertRows : function(dm, firstRow, lastRow, isUpdate){
33740         if(firstRow == 0 && lastRow == dm.getCount()-1){
33741             this.refresh();
33742         }else{
33743             if(!isUpdate){
33744                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33745             }
33746             var s = this.getScrollState();
33747             var markup = this.renderRows(firstRow, lastRow);
33748             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33749             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33750             this.restoreScroll(s);
33751             if(!isUpdate){
33752                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33753                 this.syncRowHeights(firstRow, lastRow);
33754                 this.stripeRows(firstRow);
33755                 this.layout();
33756             }
33757         }
33758     },
33759
33760     bufferRows : function(markup, target, index){
33761         var before = null, trows = target.rows, tbody = target.tBodies[0];
33762         if(index < trows.length){
33763             before = trows[index];
33764         }
33765         var b = document.createElement("div");
33766         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33767         var rows = b.firstChild.rows;
33768         for(var i = 0, len = rows.length; i < len; i++){
33769             if(before){
33770                 tbody.insertBefore(rows[0], before);
33771             }else{
33772                 tbody.appendChild(rows[0]);
33773             }
33774         }
33775         b.innerHTML = "";
33776         b = null;
33777     },
33778
33779     deleteRows : function(dm, firstRow, lastRow){
33780         if(dm.getRowCount()<1){
33781             this.fireEvent("beforerefresh", this);
33782             this.mainBody.update("");
33783             this.lockedBody.update("");
33784             this.fireEvent("refresh", this);
33785         }else{
33786             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33787             var bt = this.getBodyTable();
33788             var tbody = bt.firstChild;
33789             var rows = bt.rows;
33790             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33791                 tbody.removeChild(rows[firstRow]);
33792             }
33793             this.stripeRows(firstRow);
33794             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33795         }
33796     },
33797
33798     updateRows : function(dataSource, firstRow, lastRow){
33799         var s = this.getScrollState();
33800         this.refresh();
33801         this.restoreScroll(s);
33802     },
33803
33804     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33805         if(!noRefresh){
33806            this.refresh();
33807         }
33808         this.updateHeaderSortState();
33809     },
33810
33811     getScrollState : function(){
33812         
33813         var sb = this.scroller.dom;
33814         return {left: sb.scrollLeft, top: sb.scrollTop};
33815     },
33816
33817     stripeRows : function(startRow){
33818         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33819             return;
33820         }
33821         startRow = startRow || 0;
33822         var rows = this.getBodyTable().rows;
33823         var lrows = this.getLockedTable().rows;
33824         var cls = ' x-grid-row-alt ';
33825         for(var i = startRow, len = rows.length; i < len; i++){
33826             var row = rows[i], lrow = lrows[i];
33827             var isAlt = ((i+1) % 2 == 0);
33828             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33829             if(isAlt == hasAlt){
33830                 continue;
33831             }
33832             if(isAlt){
33833                 row.className += " x-grid-row-alt";
33834             }else{
33835                 row.className = row.className.replace("x-grid-row-alt", "");
33836             }
33837             if(lrow){
33838                 lrow.className = row.className;
33839             }
33840         }
33841     },
33842
33843     restoreScroll : function(state){
33844         //Roo.log('GridView.restoreScroll');
33845         var sb = this.scroller.dom;
33846         sb.scrollLeft = state.left;
33847         sb.scrollTop = state.top;
33848         this.syncScroll();
33849     },
33850
33851     syncScroll : function(){
33852         //Roo.log('GridView.syncScroll');
33853         var sb = this.scroller.dom;
33854         var sh = this.mainHd.dom;
33855         var bs = this.mainBody.dom;
33856         var lv = this.lockedBody.dom;
33857         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33858         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33859     },
33860
33861     handleScroll : function(e){
33862         this.syncScroll();
33863         var sb = this.scroller.dom;
33864         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33865         e.stopEvent();
33866     },
33867
33868     handleWheel : function(e){
33869         var d = e.getWheelDelta();
33870         this.scroller.dom.scrollTop -= d*22;
33871         // set this here to prevent jumpy scrolling on large tables
33872         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33873         e.stopEvent();
33874     },
33875
33876     renderRows : function(startRow, endRow){
33877         // pull in all the crap needed to render rows
33878         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33879         var colCount = cm.getColumnCount();
33880
33881         if(ds.getCount() < 1){
33882             return ["", ""];
33883         }
33884
33885         // build a map for all the columns
33886         var cs = [];
33887         for(var i = 0; i < colCount; i++){
33888             var name = cm.getDataIndex(i);
33889             cs[i] = {
33890                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33891                 renderer : cm.getRenderer(i),
33892                 id : cm.getColumnId(i),
33893                 locked : cm.isLocked(i),
33894                 has_editor : cm.isCellEditable(i)
33895             };
33896         }
33897
33898         startRow = startRow || 0;
33899         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33900
33901         // records to render
33902         var rs = ds.getRange(startRow, endRow);
33903
33904         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33905     },
33906
33907     // As much as I hate to duplicate code, this was branched because FireFox really hates
33908     // [].join("") on strings. The performance difference was substantial enough to
33909     // branch this function
33910     doRender : Roo.isGecko ?
33911             function(cs, rs, ds, startRow, colCount, stripe){
33912                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33913                 // buffers
33914                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33915                 
33916                 var hasListener = this.grid.hasListener('rowclass');
33917                 var rowcfg = {};
33918                 for(var j = 0, len = rs.length; j < len; j++){
33919                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33920                     for(var i = 0; i < colCount; i++){
33921                         c = cs[i];
33922                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33923                         p.id = c.id;
33924                         p.css = p.attr = "";
33925                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33926                         if(p.value == undefined || p.value === "") {
33927                             p.value = "&#160;";
33928                         }
33929                         if(c.has_editor){
33930                             p.css += ' x-grid-editable-cell';
33931                         }
33932                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
33933                             p.css +=  ' x-grid-dirty-cell';
33934                         }
33935                         var markup = ct.apply(p);
33936                         if(!c.locked){
33937                             cb+= markup;
33938                         }else{
33939                             lcb+= markup;
33940                         }
33941                     }
33942                     var alt = [];
33943                     if(stripe && ((rowIndex+1) % 2 == 0)){
33944                         alt.push("x-grid-row-alt")
33945                     }
33946                     if(r.dirty){
33947                         alt.push(  " x-grid-dirty-row");
33948                     }
33949                     rp.cells = lcb;
33950                     if(this.getRowClass){
33951                         alt.push(this.getRowClass(r, rowIndex));
33952                     }
33953                     if (hasListener) {
33954                         rowcfg = {
33955                              
33956                             record: r,
33957                             rowIndex : rowIndex,
33958                             rowClass : ''
33959                         };
33960                         this.grid.fireEvent('rowclass', this, rowcfg);
33961                         alt.push(rowcfg.rowClass);
33962                     }
33963                     rp.alt = alt.join(" ");
33964                     lbuf+= rt.apply(rp);
33965                     rp.cells = cb;
33966                     buf+=  rt.apply(rp);
33967                 }
33968                 return [lbuf, buf];
33969             } :
33970             function(cs, rs, ds, startRow, colCount, stripe){
33971                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33972                 // buffers
33973                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33974                 var hasListener = this.grid.hasListener('rowclass');
33975  
33976                 var rowcfg = {};
33977                 for(var j = 0, len = rs.length; j < len; j++){
33978                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33979                     for(var i = 0; i < colCount; i++){
33980                         c = cs[i];
33981                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33982                         p.id = c.id;
33983                         p.css = p.attr = "";
33984                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33985                         if(p.value == undefined || p.value === "") {
33986                             p.value = "&#160;";
33987                         }
33988                         //Roo.log(c);
33989                          if(c.has_editor){
33990                             p.css += ' x-grid-editable-cell';
33991                         }
33992                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33993                             p.css += ' x-grid-dirty-cell' 
33994                         }
33995                         
33996                         var markup = ct.apply(p);
33997                         if(!c.locked){
33998                             cb[cb.length] = markup;
33999                         }else{
34000                             lcb[lcb.length] = markup;
34001                         }
34002                     }
34003                     var alt = [];
34004                     if(stripe && ((rowIndex+1) % 2 == 0)){
34005                         alt.push( "x-grid-row-alt");
34006                     }
34007                     if(r.dirty){
34008                         alt.push(" x-grid-dirty-row");
34009                     }
34010                     rp.cells = lcb;
34011                     if(this.getRowClass){
34012                         alt.push( this.getRowClass(r, rowIndex));
34013                     }
34014                     if (hasListener) {
34015                         rowcfg = {
34016                              
34017                             record: r,
34018                             rowIndex : rowIndex,
34019                             rowClass : ''
34020                         };
34021                         this.grid.fireEvent('rowclass', this, rowcfg);
34022                         alt.push(rowcfg.rowClass);
34023                     }
34024                     
34025                     rp.alt = alt.join(" ");
34026                     rp.cells = lcb.join("");
34027                     lbuf[lbuf.length] = rt.apply(rp);
34028                     rp.cells = cb.join("");
34029                     buf[buf.length] =  rt.apply(rp);
34030                 }
34031                 return [lbuf.join(""), buf.join("")];
34032             },
34033
34034     renderBody : function(){
34035         var markup = this.renderRows();
34036         var bt = this.templates.body;
34037         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34038     },
34039
34040     /**
34041      * Refreshes the grid
34042      * @param {Boolean} headersToo
34043      */
34044     refresh : function(headersToo){
34045         this.fireEvent("beforerefresh", this);
34046         this.grid.stopEditing();
34047         var result = this.renderBody();
34048         this.lockedBody.update(result[0]);
34049         this.mainBody.update(result[1]);
34050         if(headersToo === true){
34051             this.updateHeaders();
34052             this.updateColumns();
34053             this.updateSplitters();
34054             this.updateHeaderSortState();
34055         }
34056         this.syncRowHeights();
34057         this.layout();
34058         this.fireEvent("refresh", this);
34059     },
34060
34061     handleColumnMove : function(cm, oldIndex, newIndex){
34062         this.indexMap = null;
34063         var s = this.getScrollState();
34064         this.refresh(true);
34065         this.restoreScroll(s);
34066         this.afterMove(newIndex);
34067     },
34068
34069     afterMove : function(colIndex){
34070         if(this.enableMoveAnim && Roo.enableFx){
34071             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34072         }
34073         // if multisort - fix sortOrder, and reload..
34074         if (this.grid.dataSource.multiSort) {
34075             // the we can call sort again..
34076             var dm = this.grid.dataSource;
34077             var cm = this.grid.colModel;
34078             var so = [];
34079             for(var i = 0; i < cm.config.length; i++ ) {
34080                 
34081                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34082                     continue; // dont' bother, it's not in sort list or being set.
34083                 }
34084                 
34085                 so.push(cm.config[i].dataIndex);
34086             };
34087             dm.sortOrder = so;
34088             dm.load(dm.lastOptions);
34089             
34090             
34091         }
34092         
34093     },
34094
34095     updateCell : function(dm, rowIndex, dataIndex){
34096         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34097         if(typeof colIndex == "undefined"){ // not present in grid
34098             return;
34099         }
34100         var cm = this.grid.colModel;
34101         var cell = this.getCell(rowIndex, colIndex);
34102         var cellText = this.getCellText(rowIndex, colIndex);
34103
34104         var p = {
34105             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34106             id : cm.getColumnId(colIndex),
34107             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34108         };
34109         var renderer = cm.getRenderer(colIndex);
34110         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34111         if(typeof val == "undefined" || val === "") {
34112             val = "&#160;";
34113         }
34114         cellText.innerHTML = val;
34115         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34116         this.syncRowHeights(rowIndex, rowIndex);
34117     },
34118
34119     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34120         var maxWidth = 0;
34121         if(this.grid.autoSizeHeaders){
34122             var h = this.getHeaderCellMeasure(colIndex);
34123             maxWidth = Math.max(maxWidth, h.scrollWidth);
34124         }
34125         var tb, index;
34126         if(this.cm.isLocked(colIndex)){
34127             tb = this.getLockedTable();
34128             index = colIndex;
34129         }else{
34130             tb = this.getBodyTable();
34131             index = colIndex - this.cm.getLockedCount();
34132         }
34133         if(tb && tb.rows){
34134             var rows = tb.rows;
34135             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34136             for(var i = 0; i < stopIndex; i++){
34137                 var cell = rows[i].childNodes[index].firstChild;
34138                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34139             }
34140         }
34141         return maxWidth + /*margin for error in IE*/ 5;
34142     },
34143     /**
34144      * Autofit a column to its content.
34145      * @param {Number} colIndex
34146      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34147      */
34148      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34149          if(this.cm.isHidden(colIndex)){
34150              return; // can't calc a hidden column
34151          }
34152         if(forceMinSize){
34153             var cid = this.cm.getColumnId(colIndex);
34154             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34155            if(this.grid.autoSizeHeaders){
34156                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34157            }
34158         }
34159         var newWidth = this.calcColumnWidth(colIndex);
34160         this.cm.setColumnWidth(colIndex,
34161             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34162         if(!suppressEvent){
34163             this.grid.fireEvent("columnresize", colIndex, newWidth);
34164         }
34165     },
34166
34167     /**
34168      * Autofits all columns to their content and then expands to fit any extra space in the grid
34169      */
34170      autoSizeColumns : function(){
34171         var cm = this.grid.colModel;
34172         var colCount = cm.getColumnCount();
34173         for(var i = 0; i < colCount; i++){
34174             this.autoSizeColumn(i, true, true);
34175         }
34176         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34177             this.fitColumns();
34178         }else{
34179             this.updateColumns();
34180             this.layout();
34181         }
34182     },
34183
34184     /**
34185      * Autofits all columns to the grid's width proportionate with their current size
34186      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34187      */
34188     fitColumns : function(reserveScrollSpace){
34189         var cm = this.grid.colModel;
34190         var colCount = cm.getColumnCount();
34191         var cols = [];
34192         var width = 0;
34193         var i, w;
34194         for (i = 0; i < colCount; i++){
34195             if(!cm.isHidden(i) && !cm.isFixed(i)){
34196                 w = cm.getColumnWidth(i);
34197                 cols.push(i);
34198                 cols.push(w);
34199                 width += w;
34200             }
34201         }
34202         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34203         if(reserveScrollSpace){
34204             avail -= 17;
34205         }
34206         var frac = (avail - cm.getTotalWidth())/width;
34207         while (cols.length){
34208             w = cols.pop();
34209             i = cols.pop();
34210             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34211         }
34212         this.updateColumns();
34213         this.layout();
34214     },
34215
34216     onRowSelect : function(rowIndex){
34217         var row = this.getRowComposite(rowIndex);
34218         row.addClass("x-grid-row-selected");
34219     },
34220
34221     onRowDeselect : function(rowIndex){
34222         var row = this.getRowComposite(rowIndex);
34223         row.removeClass("x-grid-row-selected");
34224     },
34225
34226     onCellSelect : function(row, col){
34227         var cell = this.getCell(row, col);
34228         if(cell){
34229             Roo.fly(cell).addClass("x-grid-cell-selected");
34230         }
34231     },
34232
34233     onCellDeselect : function(row, col){
34234         var cell = this.getCell(row, col);
34235         if(cell){
34236             Roo.fly(cell).removeClass("x-grid-cell-selected");
34237         }
34238     },
34239
34240     updateHeaderSortState : function(){
34241         
34242         // sort state can be single { field: xxx, direction : yyy}
34243         // or   { xxx=>ASC , yyy : DESC ..... }
34244         
34245         var mstate = {};
34246         if (!this.ds.multiSort) { 
34247             var state = this.ds.getSortState();
34248             if(!state){
34249                 return;
34250             }
34251             mstate[state.field] = state.direction;
34252             // FIXME... - this is not used here.. but might be elsewhere..
34253             this.sortState = state;
34254             
34255         } else {
34256             mstate = this.ds.sortToggle;
34257         }
34258         //remove existing sort classes..
34259         
34260         var sc = this.sortClasses;
34261         var hds = this.el.select(this.headerSelector).removeClass(sc);
34262         
34263         for(var f in mstate) {
34264         
34265             var sortColumn = this.cm.findColumnIndex(f);
34266             
34267             if(sortColumn != -1){
34268                 var sortDir = mstate[f];        
34269                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34270             }
34271         }
34272         
34273          
34274         
34275     },
34276
34277
34278     handleHeaderClick : function(g, index,e){
34279         
34280         Roo.log("header click");
34281         
34282         if (Roo.isTouch) {
34283             // touch events on header are handled by context
34284             this.handleHdCtx(g,index,e);
34285             return;
34286         }
34287         
34288         
34289         if(this.headersDisabled){
34290             return;
34291         }
34292         var dm = g.dataSource, cm = g.colModel;
34293         if(!cm.isSortable(index)){
34294             return;
34295         }
34296         g.stopEditing();
34297         
34298         if (dm.multiSort) {
34299             // update the sortOrder
34300             var so = [];
34301             for(var i = 0; i < cm.config.length; i++ ) {
34302                 
34303                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34304                     continue; // dont' bother, it's not in sort list or being set.
34305                 }
34306                 
34307                 so.push(cm.config[i].dataIndex);
34308             };
34309             dm.sortOrder = so;
34310         }
34311         
34312         
34313         dm.sort(cm.getDataIndex(index));
34314     },
34315
34316
34317     destroy : function(){
34318         if(this.colMenu){
34319             this.colMenu.removeAll();
34320             Roo.menu.MenuMgr.unregister(this.colMenu);
34321             this.colMenu.getEl().remove();
34322             delete this.colMenu;
34323         }
34324         if(this.hmenu){
34325             this.hmenu.removeAll();
34326             Roo.menu.MenuMgr.unregister(this.hmenu);
34327             this.hmenu.getEl().remove();
34328             delete this.hmenu;
34329         }
34330         if(this.grid.enableColumnMove){
34331             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34332             if(dds){
34333                 for(var dd in dds){
34334                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34335                         var elid = dds[dd].dragElId;
34336                         dds[dd].unreg();
34337                         Roo.get(elid).remove();
34338                     } else if(dds[dd].config.isTarget){
34339                         dds[dd].proxyTop.remove();
34340                         dds[dd].proxyBottom.remove();
34341                         dds[dd].unreg();
34342                     }
34343                     if(Roo.dd.DDM.locationCache[dd]){
34344                         delete Roo.dd.DDM.locationCache[dd];
34345                     }
34346                 }
34347                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34348             }
34349         }
34350         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34351         this.bind(null, null);
34352         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34353     },
34354
34355     handleLockChange : function(){
34356         this.refresh(true);
34357     },
34358
34359     onDenyColumnLock : function(){
34360
34361     },
34362
34363     onDenyColumnHide : function(){
34364
34365     },
34366
34367     handleHdMenuClick : function(item){
34368         var index = this.hdCtxIndex;
34369         var cm = this.cm, ds = this.ds;
34370         switch(item.id){
34371             case "asc":
34372                 ds.sort(cm.getDataIndex(index), "ASC");
34373                 break;
34374             case "desc":
34375                 ds.sort(cm.getDataIndex(index), "DESC");
34376                 break;
34377             case "lock":
34378                 var lc = cm.getLockedCount();
34379                 if(cm.getColumnCount(true) <= lc+1){
34380                     this.onDenyColumnLock();
34381                     return;
34382                 }
34383                 if(lc != index){
34384                     cm.setLocked(index, true, true);
34385                     cm.moveColumn(index, lc);
34386                     this.grid.fireEvent("columnmove", index, lc);
34387                 }else{
34388                     cm.setLocked(index, true);
34389                 }
34390             break;
34391             case "unlock":
34392                 var lc = cm.getLockedCount();
34393                 if((lc-1) != index){
34394                     cm.setLocked(index, false, true);
34395                     cm.moveColumn(index, lc-1);
34396                     this.grid.fireEvent("columnmove", index, lc-1);
34397                 }else{
34398                     cm.setLocked(index, false);
34399                 }
34400             break;
34401             case 'wider': // used to expand cols on touch..
34402             case 'narrow':
34403                 var cw = cm.getColumnWidth(index);
34404                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34405                 cw = Math.max(0, cw);
34406                 cw = Math.min(cw,4000);
34407                 cm.setColumnWidth(index, cw);
34408                 break;
34409                 
34410             default:
34411                 index = cm.getIndexById(item.id.substr(4));
34412                 if(index != -1){
34413                     if(item.checked && cm.getColumnCount(true) <= 1){
34414                         this.onDenyColumnHide();
34415                         return false;
34416                     }
34417                     cm.setHidden(index, item.checked);
34418                 }
34419         }
34420         return true;
34421     },
34422
34423     beforeColMenuShow : function(){
34424         var cm = this.cm,  colCount = cm.getColumnCount();
34425         this.colMenu.removeAll();
34426         for(var i = 0; i < colCount; i++){
34427             this.colMenu.add(new Roo.menu.CheckItem({
34428                 id: "col-"+cm.getColumnId(i),
34429                 text: cm.getColumnHeader(i),
34430                 checked: !cm.isHidden(i),
34431                 hideOnClick:false
34432             }));
34433         }
34434     },
34435
34436     handleHdCtx : function(g, index, e){
34437         e.stopEvent();
34438         var hd = this.getHeaderCell(index);
34439         this.hdCtxIndex = index;
34440         var ms = this.hmenu.items, cm = this.cm;
34441         ms.get("asc").setDisabled(!cm.isSortable(index));
34442         ms.get("desc").setDisabled(!cm.isSortable(index));
34443         if(this.grid.enableColLock !== false){
34444             ms.get("lock").setDisabled(cm.isLocked(index));
34445             ms.get("unlock").setDisabled(!cm.isLocked(index));
34446         }
34447         this.hmenu.show(hd, "tl-bl");
34448     },
34449
34450     handleHdOver : function(e){
34451         var hd = this.findHeaderCell(e.getTarget());
34452         if(hd && !this.headersDisabled){
34453             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34454                this.fly(hd).addClass("x-grid-hd-over");
34455             }
34456         }
34457     },
34458
34459     handleHdOut : function(e){
34460         var hd = this.findHeaderCell(e.getTarget());
34461         if(hd){
34462             this.fly(hd).removeClass("x-grid-hd-over");
34463         }
34464     },
34465
34466     handleSplitDblClick : function(e, t){
34467         var i = this.getCellIndex(t);
34468         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34469             this.autoSizeColumn(i, true);
34470             this.layout();
34471         }
34472     },
34473
34474     render : function(){
34475
34476         var cm = this.cm;
34477         var colCount = cm.getColumnCount();
34478
34479         if(this.grid.monitorWindowResize === true){
34480             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34481         }
34482         var header = this.renderHeaders();
34483         var body = this.templates.body.apply({rows:""});
34484         var html = this.templates.master.apply({
34485             lockedBody: body,
34486             body: body,
34487             lockedHeader: header[0],
34488             header: header[1]
34489         });
34490
34491         //this.updateColumns();
34492
34493         this.grid.getGridEl().dom.innerHTML = html;
34494
34495         this.initElements();
34496         
34497         // a kludge to fix the random scolling effect in webkit
34498         this.el.on("scroll", function() {
34499             this.el.dom.scrollTop=0; // hopefully not recursive..
34500         },this);
34501
34502         this.scroller.on("scroll", this.handleScroll, this);
34503         this.lockedBody.on("mousewheel", this.handleWheel, this);
34504         this.mainBody.on("mousewheel", this.handleWheel, this);
34505
34506         this.mainHd.on("mouseover", this.handleHdOver, this);
34507         this.mainHd.on("mouseout", this.handleHdOut, this);
34508         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34509                 {delegate: "."+this.splitClass});
34510
34511         this.lockedHd.on("mouseover", this.handleHdOver, this);
34512         this.lockedHd.on("mouseout", this.handleHdOut, this);
34513         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34514                 {delegate: "."+this.splitClass});
34515
34516         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34517             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34518         }
34519
34520         this.updateSplitters();
34521
34522         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34523             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34524             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34525         }
34526
34527         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34528             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34529             this.hmenu.add(
34530                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34531                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34532             );
34533             if(this.grid.enableColLock !== false){
34534                 this.hmenu.add('-',
34535                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34536                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34537                 );
34538             }
34539             if (Roo.isTouch) {
34540                  this.hmenu.add('-',
34541                     {id:"wider", text: this.columnsWiderText},
34542                     {id:"narrow", text: this.columnsNarrowText }
34543                 );
34544                 
34545                  
34546             }
34547             
34548             if(this.grid.enableColumnHide !== false){
34549
34550                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34551                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34552                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34553
34554                 this.hmenu.add('-',
34555                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34556                 );
34557             }
34558             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34559
34560             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34561         }
34562
34563         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34564             this.dd = new Roo.grid.GridDragZone(this.grid, {
34565                 ddGroup : this.grid.ddGroup || 'GridDD'
34566             });
34567             
34568         }
34569
34570         /*
34571         for(var i = 0; i < colCount; i++){
34572             if(cm.isHidden(i)){
34573                 this.hideColumn(i);
34574             }
34575             if(cm.config[i].align){
34576                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34577                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34578             }
34579         }*/
34580         
34581         this.updateHeaderSortState();
34582
34583         this.beforeInitialResize();
34584         this.layout(true);
34585
34586         // two part rendering gives faster view to the user
34587         this.renderPhase2.defer(1, this);
34588     },
34589
34590     renderPhase2 : function(){
34591         // render the rows now
34592         this.refresh();
34593         if(this.grid.autoSizeColumns){
34594             this.autoSizeColumns();
34595         }
34596     },
34597
34598     beforeInitialResize : function(){
34599
34600     },
34601
34602     onColumnSplitterMoved : function(i, w){
34603         this.userResized = true;
34604         var cm = this.grid.colModel;
34605         cm.setColumnWidth(i, w, true);
34606         var cid = cm.getColumnId(i);
34607         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34608         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34609         this.updateSplitters();
34610         this.layout();
34611         this.grid.fireEvent("columnresize", i, w);
34612     },
34613
34614     syncRowHeights : function(startIndex, endIndex){
34615         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34616             startIndex = startIndex || 0;
34617             var mrows = this.getBodyTable().rows;
34618             var lrows = this.getLockedTable().rows;
34619             var len = mrows.length-1;
34620             endIndex = Math.min(endIndex || len, len);
34621             for(var i = startIndex; i <= endIndex; i++){
34622                 var m = mrows[i], l = lrows[i];
34623                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34624                 m.style.height = l.style.height = h + "px";
34625             }
34626         }
34627     },
34628
34629     layout : function(initialRender, is2ndPass){
34630         var g = this.grid;
34631         var auto = g.autoHeight;
34632         var scrollOffset = 16;
34633         var c = g.getGridEl(), cm = this.cm,
34634                 expandCol = g.autoExpandColumn,
34635                 gv = this;
34636         //c.beginMeasure();
34637
34638         if(!c.dom.offsetWidth){ // display:none?
34639             if(initialRender){
34640                 this.lockedWrap.show();
34641                 this.mainWrap.show();
34642             }
34643             return;
34644         }
34645
34646         var hasLock = this.cm.isLocked(0);
34647
34648         var tbh = this.headerPanel.getHeight();
34649         var bbh = this.footerPanel.getHeight();
34650
34651         if(auto){
34652             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34653             var newHeight = ch + c.getBorderWidth("tb");
34654             if(g.maxHeight){
34655                 newHeight = Math.min(g.maxHeight, newHeight);
34656             }
34657             c.setHeight(newHeight);
34658         }
34659
34660         if(g.autoWidth){
34661             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34662         }
34663
34664         var s = this.scroller;
34665
34666         var csize = c.getSize(true);
34667
34668         this.el.setSize(csize.width, csize.height);
34669
34670         this.headerPanel.setWidth(csize.width);
34671         this.footerPanel.setWidth(csize.width);
34672
34673         var hdHeight = this.mainHd.getHeight();
34674         var vw = csize.width;
34675         var vh = csize.height - (tbh + bbh);
34676
34677         s.setSize(vw, vh);
34678
34679         var bt = this.getBodyTable();
34680         
34681         if(cm.getLockedCount() == cm.config.length){
34682             bt = this.getLockedTable();
34683         }
34684         
34685         var ltWidth = hasLock ?
34686                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34687
34688         var scrollHeight = bt.offsetHeight;
34689         var scrollWidth = ltWidth + bt.offsetWidth;
34690         var vscroll = false, hscroll = false;
34691
34692         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34693
34694         var lw = this.lockedWrap, mw = this.mainWrap;
34695         var lb = this.lockedBody, mb = this.mainBody;
34696
34697         setTimeout(function(){
34698             var t = s.dom.offsetTop;
34699             var w = s.dom.clientWidth,
34700                 h = s.dom.clientHeight;
34701
34702             lw.setTop(t);
34703             lw.setSize(ltWidth, h);
34704
34705             mw.setLeftTop(ltWidth, t);
34706             mw.setSize(w-ltWidth, h);
34707
34708             lb.setHeight(h-hdHeight);
34709             mb.setHeight(h-hdHeight);
34710
34711             if(is2ndPass !== true && !gv.userResized && expandCol){
34712                 // high speed resize without full column calculation
34713                 
34714                 var ci = cm.getIndexById(expandCol);
34715                 if (ci < 0) {
34716                     ci = cm.findColumnIndex(expandCol);
34717                 }
34718                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34719                 var expandId = cm.getColumnId(ci);
34720                 var  tw = cm.getTotalWidth(false);
34721                 var currentWidth = cm.getColumnWidth(ci);
34722                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34723                 if(currentWidth != cw){
34724                     cm.setColumnWidth(ci, cw, true);
34725                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34726                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34727                     gv.updateSplitters();
34728                     gv.layout(false, true);
34729                 }
34730             }
34731
34732             if(initialRender){
34733                 lw.show();
34734                 mw.show();
34735             }
34736             //c.endMeasure();
34737         }, 10);
34738     },
34739
34740     onWindowResize : function(){
34741         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34742             return;
34743         }
34744         this.layout();
34745     },
34746
34747     appendFooter : function(parentEl){
34748         return null;
34749     },
34750
34751     sortAscText : "Sort Ascending",
34752     sortDescText : "Sort Descending",
34753     lockText : "Lock Column",
34754     unlockText : "Unlock Column",
34755     columnsText : "Columns",
34756  
34757     columnsWiderText : "Wider",
34758     columnsNarrowText : "Thinner"
34759 });
34760
34761
34762 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34763     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34764     this.proxy.el.addClass('x-grid3-col-dd');
34765 };
34766
34767 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34768     handleMouseDown : function(e){
34769
34770     },
34771
34772     callHandleMouseDown : function(e){
34773         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34774     }
34775 });
34776 /*
34777  * Based on:
34778  * Ext JS Library 1.1.1
34779  * Copyright(c) 2006-2007, Ext JS, LLC.
34780  *
34781  * Originally Released Under LGPL - original licence link has changed is not relivant.
34782  *
34783  * Fork - LGPL
34784  * <script type="text/javascript">
34785  */
34786  
34787 // private
34788 // This is a support class used internally by the Grid components
34789 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34790     this.grid = grid;
34791     this.view = grid.getView();
34792     this.proxy = this.view.resizeProxy;
34793     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34794         "gridSplitters" + this.grid.getGridEl().id, {
34795         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34796     });
34797     this.setHandleElId(Roo.id(hd));
34798     this.setOuterHandleElId(Roo.id(hd2));
34799     this.scroll = false;
34800 };
34801 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34802     fly: Roo.Element.fly,
34803
34804     b4StartDrag : function(x, y){
34805         this.view.headersDisabled = true;
34806         this.proxy.setHeight(this.view.mainWrap.getHeight());
34807         var w = this.cm.getColumnWidth(this.cellIndex);
34808         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34809         this.resetConstraints();
34810         this.setXConstraint(minw, 1000);
34811         this.setYConstraint(0, 0);
34812         this.minX = x - minw;
34813         this.maxX = x + 1000;
34814         this.startPos = x;
34815         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34816     },
34817
34818
34819     handleMouseDown : function(e){
34820         ev = Roo.EventObject.setEvent(e);
34821         var t = this.fly(ev.getTarget());
34822         if(t.hasClass("x-grid-split")){
34823             this.cellIndex = this.view.getCellIndex(t.dom);
34824             this.split = t.dom;
34825             this.cm = this.grid.colModel;
34826             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34827                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34828             }
34829         }
34830     },
34831
34832     endDrag : function(e){
34833         this.view.headersDisabled = false;
34834         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34835         var diff = endX - this.startPos;
34836         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34837     },
34838
34839     autoOffset : function(){
34840         this.setDelta(0,0);
34841     }
34842 });/*
34843  * Based on:
34844  * Ext JS Library 1.1.1
34845  * Copyright(c) 2006-2007, Ext JS, LLC.
34846  *
34847  * Originally Released Under LGPL - original licence link has changed is not relivant.
34848  *
34849  * Fork - LGPL
34850  * <script type="text/javascript">
34851  */
34852  
34853 // private
34854 // This is a support class used internally by the Grid components
34855 Roo.grid.GridDragZone = function(grid, config){
34856     this.view = grid.getView();
34857     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34858     if(this.view.lockedBody){
34859         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34860         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34861     }
34862     this.scroll = false;
34863     this.grid = grid;
34864     this.ddel = document.createElement('div');
34865     this.ddel.className = 'x-grid-dd-wrap';
34866 };
34867
34868 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34869     ddGroup : "GridDD",
34870
34871     getDragData : function(e){
34872         var t = Roo.lib.Event.getTarget(e);
34873         var rowIndex = this.view.findRowIndex(t);
34874         var sm = this.grid.selModel;
34875             
34876         //Roo.log(rowIndex);
34877         
34878         if (sm.getSelectedCell) {
34879             // cell selection..
34880             if (!sm.getSelectedCell()) {
34881                 return false;
34882             }
34883             if (rowIndex != sm.getSelectedCell()[0]) {
34884                 return false;
34885             }
34886         
34887         }
34888         
34889         if(rowIndex !== false){
34890             
34891             // if editorgrid.. 
34892             
34893             
34894             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
34895                
34896             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34897               //  
34898             //}
34899             if (e.hasModifier()){
34900                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34901             }
34902             
34903             Roo.log("getDragData");
34904             
34905             return {
34906                 grid: this.grid,
34907                 ddel: this.ddel,
34908                 rowIndex: rowIndex,
34909                 selections:sm.getSelections ? sm.getSelections() : (
34910                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
34911                 )
34912             };
34913         }
34914         return false;
34915     },
34916
34917     onInitDrag : function(e){
34918         var data = this.dragData;
34919         this.ddel.innerHTML = this.grid.getDragDropText();
34920         this.proxy.update(this.ddel);
34921         // fire start drag?
34922     },
34923
34924     afterRepair : function(){
34925         this.dragging = false;
34926     },
34927
34928     getRepairXY : function(e, data){
34929         return false;
34930     },
34931
34932     onEndDrag : function(data, e){
34933         // fire end drag?
34934     },
34935
34936     onValidDrop : function(dd, e, id){
34937         // fire drag drop?
34938         this.hideProxy();
34939     },
34940
34941     beforeInvalidDrop : function(e, id){
34942
34943     }
34944 });/*
34945  * Based on:
34946  * Ext JS Library 1.1.1
34947  * Copyright(c) 2006-2007, Ext JS, LLC.
34948  *
34949  * Originally Released Under LGPL - original licence link has changed is not relivant.
34950  *
34951  * Fork - LGPL
34952  * <script type="text/javascript">
34953  */
34954  
34955
34956 /**
34957  * @class Roo.grid.ColumnModel
34958  * @extends Roo.util.Observable
34959  * This is the default implementation of a ColumnModel used by the Grid. It defines
34960  * the columns in the grid.
34961  * <br>Usage:<br>
34962  <pre><code>
34963  var colModel = new Roo.grid.ColumnModel([
34964         {header: "Ticker", width: 60, sortable: true, locked: true},
34965         {header: "Company Name", width: 150, sortable: true},
34966         {header: "Market Cap.", width: 100, sortable: true},
34967         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34968         {header: "Employees", width: 100, sortable: true, resizable: false}
34969  ]);
34970  </code></pre>
34971  * <p>
34972  
34973  * The config options listed for this class are options which may appear in each
34974  * individual column definition.
34975  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34976  * @constructor
34977  * @param {Object} config An Array of column config objects. See this class's
34978  * config objects for details.
34979 */
34980 Roo.grid.ColumnModel = function(config){
34981         /**
34982      * The config passed into the constructor
34983      */
34984     this.config = config;
34985     this.lookup = {};
34986
34987     // if no id, create one
34988     // if the column does not have a dataIndex mapping,
34989     // map it to the order it is in the config
34990     for(var i = 0, len = config.length; i < len; i++){
34991         var c = config[i];
34992         if(typeof c.dataIndex == "undefined"){
34993             c.dataIndex = i;
34994         }
34995         if(typeof c.renderer == "string"){
34996             c.renderer = Roo.util.Format[c.renderer];
34997         }
34998         if(typeof c.id == "undefined"){
34999             c.id = Roo.id();
35000         }
35001         if(c.editor && c.editor.xtype){
35002             c.editor  = Roo.factory(c.editor, Roo.grid);
35003         }
35004         if(c.editor && c.editor.isFormField){
35005             c.editor = new Roo.grid.GridEditor(c.editor);
35006         }
35007         this.lookup[c.id] = c;
35008     }
35009
35010     /**
35011      * The width of columns which have no width specified (defaults to 100)
35012      * @type Number
35013      */
35014     this.defaultWidth = 100;
35015
35016     /**
35017      * Default sortable of columns which have no sortable specified (defaults to false)
35018      * @type Boolean
35019      */
35020     this.defaultSortable = false;
35021
35022     this.addEvents({
35023         /**
35024              * @event widthchange
35025              * Fires when the width of a column changes.
35026              * @param {ColumnModel} this
35027              * @param {Number} columnIndex The column index
35028              * @param {Number} newWidth The new width
35029              */
35030             "widthchange": true,
35031         /**
35032              * @event headerchange
35033              * Fires when the text of a header changes.
35034              * @param {ColumnModel} this
35035              * @param {Number} columnIndex The column index
35036              * @param {Number} newText The new header text
35037              */
35038             "headerchange": true,
35039         /**
35040              * @event hiddenchange
35041              * Fires when a column is hidden or "unhidden".
35042              * @param {ColumnModel} this
35043              * @param {Number} columnIndex The column index
35044              * @param {Boolean} hidden true if hidden, false otherwise
35045              */
35046             "hiddenchange": true,
35047             /**
35048          * @event columnmoved
35049          * Fires when a column is moved.
35050          * @param {ColumnModel} this
35051          * @param {Number} oldIndex
35052          * @param {Number} newIndex
35053          */
35054         "columnmoved" : true,
35055         /**
35056          * @event columlockchange
35057          * Fires when a column's locked state is changed
35058          * @param {ColumnModel} this
35059          * @param {Number} colIndex
35060          * @param {Boolean} locked true if locked
35061          */
35062         "columnlockchange" : true
35063     });
35064     Roo.grid.ColumnModel.superclass.constructor.call(this);
35065 };
35066 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35067     /**
35068      * @cfg {String} header The header text to display in the Grid view.
35069      */
35070     /**
35071      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35072      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35073      * specified, the column's index is used as an index into the Record's data Array.
35074      */
35075     /**
35076      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35077      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35078      */
35079     /**
35080      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35081      * Defaults to the value of the {@link #defaultSortable} property.
35082      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35083      */
35084     /**
35085      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35086      */
35087     /**
35088      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35089      */
35090     /**
35091      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35092      */
35093     /**
35094      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35095      */
35096     /**
35097      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35098      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35099      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35100      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35101      */
35102        /**
35103      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35104      */
35105     /**
35106      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35107      */
35108     /**
35109      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35110      */
35111     /**
35112      * @cfg {String} cursor (Optional)
35113      */
35114     /**
35115      * @cfg {String} tooltip (Optional)
35116      */
35117     /**
35118      * @cfg {Number} xs (Optional)
35119      */
35120     /**
35121      * @cfg {Number} sm (Optional)
35122      */
35123     /**
35124      * @cfg {Number} md (Optional)
35125      */
35126     /**
35127      * @cfg {Number} lg (Optional)
35128      */
35129     /**
35130      * Returns the id of the column at the specified index.
35131      * @param {Number} index The column index
35132      * @return {String} the id
35133      */
35134     getColumnId : function(index){
35135         return this.config[index].id;
35136     },
35137
35138     /**
35139      * Returns the column for a specified id.
35140      * @param {String} id The column id
35141      * @return {Object} the column
35142      */
35143     getColumnById : function(id){
35144         return this.lookup[id];
35145     },
35146
35147     
35148     /**
35149      * Returns the column for a specified dataIndex.
35150      * @param {String} dataIndex The column dataIndex
35151      * @return {Object|Boolean} the column or false if not found
35152      */
35153     getColumnByDataIndex: function(dataIndex){
35154         var index = this.findColumnIndex(dataIndex);
35155         return index > -1 ? this.config[index] : false;
35156     },
35157     
35158     /**
35159      * Returns the index for a specified column id.
35160      * @param {String} id The column id
35161      * @return {Number} the index, or -1 if not found
35162      */
35163     getIndexById : function(id){
35164         for(var i = 0, len = this.config.length; i < len; i++){
35165             if(this.config[i].id == id){
35166                 return i;
35167             }
35168         }
35169         return -1;
35170     },
35171     
35172     /**
35173      * Returns the index for a specified column dataIndex.
35174      * @param {String} dataIndex The column dataIndex
35175      * @return {Number} the index, or -1 if not found
35176      */
35177     
35178     findColumnIndex : function(dataIndex){
35179         for(var i = 0, len = this.config.length; i < len; i++){
35180             if(this.config[i].dataIndex == dataIndex){
35181                 return i;
35182             }
35183         }
35184         return -1;
35185     },
35186     
35187     
35188     moveColumn : function(oldIndex, newIndex){
35189         var c = this.config[oldIndex];
35190         this.config.splice(oldIndex, 1);
35191         this.config.splice(newIndex, 0, c);
35192         this.dataMap = null;
35193         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35194     },
35195
35196     isLocked : function(colIndex){
35197         return this.config[colIndex].locked === true;
35198     },
35199
35200     setLocked : function(colIndex, value, suppressEvent){
35201         if(this.isLocked(colIndex) == value){
35202             return;
35203         }
35204         this.config[colIndex].locked = value;
35205         if(!suppressEvent){
35206             this.fireEvent("columnlockchange", this, colIndex, value);
35207         }
35208     },
35209
35210     getTotalLockedWidth : function(){
35211         var totalWidth = 0;
35212         for(var i = 0; i < this.config.length; i++){
35213             if(this.isLocked(i) && !this.isHidden(i)){
35214                 this.totalWidth += this.getColumnWidth(i);
35215             }
35216         }
35217         return totalWidth;
35218     },
35219
35220     getLockedCount : function(){
35221         for(var i = 0, len = this.config.length; i < len; i++){
35222             if(!this.isLocked(i)){
35223                 return i;
35224             }
35225         }
35226         
35227         return this.config.length;
35228     },
35229
35230     /**
35231      * Returns the number of columns.
35232      * @return {Number}
35233      */
35234     getColumnCount : function(visibleOnly){
35235         if(visibleOnly === true){
35236             var c = 0;
35237             for(var i = 0, len = this.config.length; i < len; i++){
35238                 if(!this.isHidden(i)){
35239                     c++;
35240                 }
35241             }
35242             return c;
35243         }
35244         return this.config.length;
35245     },
35246
35247     /**
35248      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35249      * @param {Function} fn
35250      * @param {Object} scope (optional)
35251      * @return {Array} result
35252      */
35253     getColumnsBy : function(fn, scope){
35254         var r = [];
35255         for(var i = 0, len = this.config.length; i < len; i++){
35256             var c = this.config[i];
35257             if(fn.call(scope||this, c, i) === true){
35258                 r[r.length] = c;
35259             }
35260         }
35261         return r;
35262     },
35263
35264     /**
35265      * Returns true if the specified column is sortable.
35266      * @param {Number} col The column index
35267      * @return {Boolean}
35268      */
35269     isSortable : function(col){
35270         if(typeof this.config[col].sortable == "undefined"){
35271             return this.defaultSortable;
35272         }
35273         return this.config[col].sortable;
35274     },
35275
35276     /**
35277      * Returns the rendering (formatting) function defined for the column.
35278      * @param {Number} col The column index.
35279      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35280      */
35281     getRenderer : function(col){
35282         if(!this.config[col].renderer){
35283             return Roo.grid.ColumnModel.defaultRenderer;
35284         }
35285         return this.config[col].renderer;
35286     },
35287
35288     /**
35289      * Sets the rendering (formatting) function for a column.
35290      * @param {Number} col The column index
35291      * @param {Function} fn The function to use to process the cell's raw data
35292      * to return HTML markup for the grid view. The render function is called with
35293      * the following parameters:<ul>
35294      * <li>Data value.</li>
35295      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35296      * <li>css A CSS style string to apply to the table cell.</li>
35297      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35298      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35299      * <li>Row index</li>
35300      * <li>Column index</li>
35301      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35302      */
35303     setRenderer : function(col, fn){
35304         this.config[col].renderer = fn;
35305     },
35306
35307     /**
35308      * Returns the width for the specified column.
35309      * @param {Number} col The column index
35310      * @return {Number}
35311      */
35312     getColumnWidth : function(col){
35313         return this.config[col].width * 1 || this.defaultWidth;
35314     },
35315
35316     /**
35317      * Sets the width for a column.
35318      * @param {Number} col The column index
35319      * @param {Number} width The new width
35320      */
35321     setColumnWidth : function(col, width, suppressEvent){
35322         this.config[col].width = width;
35323         this.totalWidth = null;
35324         if(!suppressEvent){
35325              this.fireEvent("widthchange", this, col, width);
35326         }
35327     },
35328
35329     /**
35330      * Returns the total width of all columns.
35331      * @param {Boolean} includeHidden True to include hidden column widths
35332      * @return {Number}
35333      */
35334     getTotalWidth : function(includeHidden){
35335         if(!this.totalWidth){
35336             this.totalWidth = 0;
35337             for(var i = 0, len = this.config.length; i < len; i++){
35338                 if(includeHidden || !this.isHidden(i)){
35339                     this.totalWidth += this.getColumnWidth(i);
35340                 }
35341             }
35342         }
35343         return this.totalWidth;
35344     },
35345
35346     /**
35347      * Returns the header for the specified column.
35348      * @param {Number} col The column index
35349      * @return {String}
35350      */
35351     getColumnHeader : function(col){
35352         return this.config[col].header;
35353     },
35354
35355     /**
35356      * Sets the header for a column.
35357      * @param {Number} col The column index
35358      * @param {String} header The new header
35359      */
35360     setColumnHeader : function(col, header){
35361         this.config[col].header = header;
35362         this.fireEvent("headerchange", this, col, header);
35363     },
35364
35365     /**
35366      * Returns the tooltip for the specified column.
35367      * @param {Number} col The column index
35368      * @return {String}
35369      */
35370     getColumnTooltip : function(col){
35371             return this.config[col].tooltip;
35372     },
35373     /**
35374      * Sets the tooltip for a column.
35375      * @param {Number} col The column index
35376      * @param {String} tooltip The new tooltip
35377      */
35378     setColumnTooltip : function(col, tooltip){
35379             this.config[col].tooltip = tooltip;
35380     },
35381
35382     /**
35383      * Returns the dataIndex for the specified column.
35384      * @param {Number} col The column index
35385      * @return {Number}
35386      */
35387     getDataIndex : function(col){
35388         return this.config[col].dataIndex;
35389     },
35390
35391     /**
35392      * Sets the dataIndex for a column.
35393      * @param {Number} col The column index
35394      * @param {Number} dataIndex The new dataIndex
35395      */
35396     setDataIndex : function(col, dataIndex){
35397         this.config[col].dataIndex = dataIndex;
35398     },
35399
35400     
35401     
35402     /**
35403      * Returns true if the cell is editable.
35404      * @param {Number} colIndex The column index
35405      * @param {Number} rowIndex The row index - this is nto actually used..?
35406      * @return {Boolean}
35407      */
35408     isCellEditable : function(colIndex, rowIndex){
35409         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35410     },
35411
35412     /**
35413      * Returns the editor defined for the cell/column.
35414      * return false or null to disable editing.
35415      * @param {Number} colIndex The column index
35416      * @param {Number} rowIndex The row index
35417      * @return {Object}
35418      */
35419     getCellEditor : function(colIndex, rowIndex){
35420         return this.config[colIndex].editor;
35421     },
35422
35423     /**
35424      * Sets if a column is editable.
35425      * @param {Number} col The column index
35426      * @param {Boolean} editable True if the column is editable
35427      */
35428     setEditable : function(col, editable){
35429         this.config[col].editable = editable;
35430     },
35431
35432
35433     /**
35434      * Returns true if the column is hidden.
35435      * @param {Number} colIndex The column index
35436      * @return {Boolean}
35437      */
35438     isHidden : function(colIndex){
35439         return this.config[colIndex].hidden;
35440     },
35441
35442
35443     /**
35444      * Returns true if the column width cannot be changed
35445      */
35446     isFixed : function(colIndex){
35447         return this.config[colIndex].fixed;
35448     },
35449
35450     /**
35451      * Returns true if the column can be resized
35452      * @return {Boolean}
35453      */
35454     isResizable : function(colIndex){
35455         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35456     },
35457     /**
35458      * Sets if a column is hidden.
35459      * @param {Number} colIndex The column index
35460      * @param {Boolean} hidden True if the column is hidden
35461      */
35462     setHidden : function(colIndex, hidden){
35463         this.config[colIndex].hidden = hidden;
35464         this.totalWidth = null;
35465         this.fireEvent("hiddenchange", this, colIndex, hidden);
35466     },
35467
35468     /**
35469      * Sets the editor for a column.
35470      * @param {Number} col The column index
35471      * @param {Object} editor The editor object
35472      */
35473     setEditor : function(col, editor){
35474         this.config[col].editor = editor;
35475     }
35476 });
35477
35478 Roo.grid.ColumnModel.defaultRenderer = function(value)
35479 {
35480     if(typeof value == "object") {
35481         return value;
35482     }
35483         if(typeof value == "string" && value.length < 1){
35484             return "&#160;";
35485         }
35486     
35487         return String.format("{0}", value);
35488 };
35489
35490 // Alias for backwards compatibility
35491 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35492 /*
35493  * Based on:
35494  * Ext JS Library 1.1.1
35495  * Copyright(c) 2006-2007, Ext JS, LLC.
35496  *
35497  * Originally Released Under LGPL - original licence link has changed is not relivant.
35498  *
35499  * Fork - LGPL
35500  * <script type="text/javascript">
35501  */
35502
35503 /**
35504  * @class Roo.grid.AbstractSelectionModel
35505  * @extends Roo.util.Observable
35506  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35507  * implemented by descendant classes.  This class should not be directly instantiated.
35508  * @constructor
35509  */
35510 Roo.grid.AbstractSelectionModel = function(){
35511     this.locked = false;
35512     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35513 };
35514
35515 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35516     /** @ignore Called by the grid automatically. Do not call directly. */
35517     init : function(grid){
35518         this.grid = grid;
35519         this.initEvents();
35520     },
35521
35522     /**
35523      * Locks the selections.
35524      */
35525     lock : function(){
35526         this.locked = true;
35527     },
35528
35529     /**
35530      * Unlocks the selections.
35531      */
35532     unlock : function(){
35533         this.locked = false;
35534     },
35535
35536     /**
35537      * Returns true if the selections are locked.
35538      * @return {Boolean}
35539      */
35540     isLocked : function(){
35541         return this.locked;
35542     }
35543 });/*
35544  * Based on:
35545  * Ext JS Library 1.1.1
35546  * Copyright(c) 2006-2007, Ext JS, LLC.
35547  *
35548  * Originally Released Under LGPL - original licence link has changed is not relivant.
35549  *
35550  * Fork - LGPL
35551  * <script type="text/javascript">
35552  */
35553 /**
35554  * @extends Roo.grid.AbstractSelectionModel
35555  * @class Roo.grid.RowSelectionModel
35556  * The default SelectionModel used by {@link Roo.grid.Grid}.
35557  * It supports multiple selections and keyboard selection/navigation. 
35558  * @constructor
35559  * @param {Object} config
35560  */
35561 Roo.grid.RowSelectionModel = function(config){
35562     Roo.apply(this, config);
35563     this.selections = new Roo.util.MixedCollection(false, function(o){
35564         return o.id;
35565     });
35566
35567     this.last = false;
35568     this.lastActive = false;
35569
35570     this.addEvents({
35571         /**
35572              * @event selectionchange
35573              * Fires when the selection changes
35574              * @param {SelectionModel} this
35575              */
35576             "selectionchange" : true,
35577         /**
35578              * @event afterselectionchange
35579              * Fires after the selection changes (eg. by key press or clicking)
35580              * @param {SelectionModel} this
35581              */
35582             "afterselectionchange" : true,
35583         /**
35584              * @event beforerowselect
35585              * Fires when a row is selected being selected, return false to cancel.
35586              * @param {SelectionModel} this
35587              * @param {Number} rowIndex The selected index
35588              * @param {Boolean} keepExisting False if other selections will be cleared
35589              */
35590             "beforerowselect" : true,
35591         /**
35592              * @event rowselect
35593              * Fires when a row is selected.
35594              * @param {SelectionModel} this
35595              * @param {Number} rowIndex The selected index
35596              * @param {Roo.data.Record} r The record
35597              */
35598             "rowselect" : true,
35599         /**
35600              * @event rowdeselect
35601              * Fires when a row is deselected.
35602              * @param {SelectionModel} this
35603              * @param {Number} rowIndex The selected index
35604              */
35605         "rowdeselect" : true
35606     });
35607     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35608     this.locked = false;
35609 };
35610
35611 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35612     /**
35613      * @cfg {Boolean} singleSelect
35614      * True to allow selection of only one row at a time (defaults to false)
35615      */
35616     singleSelect : false,
35617
35618     // private
35619     initEvents : function(){
35620
35621         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35622             this.grid.on("mousedown", this.handleMouseDown, this);
35623         }else{ // allow click to work like normal
35624             this.grid.on("rowclick", this.handleDragableRowClick, this);
35625         }
35626
35627         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35628             "up" : function(e){
35629                 if(!e.shiftKey){
35630                     this.selectPrevious(e.shiftKey);
35631                 }else if(this.last !== false && this.lastActive !== false){
35632                     var last = this.last;
35633                     this.selectRange(this.last,  this.lastActive-1);
35634                     this.grid.getView().focusRow(this.lastActive);
35635                     if(last !== false){
35636                         this.last = last;
35637                     }
35638                 }else{
35639                     this.selectFirstRow();
35640                 }
35641                 this.fireEvent("afterselectionchange", this);
35642             },
35643             "down" : function(e){
35644                 if(!e.shiftKey){
35645                     this.selectNext(e.shiftKey);
35646                 }else if(this.last !== false && this.lastActive !== false){
35647                     var last = this.last;
35648                     this.selectRange(this.last,  this.lastActive+1);
35649                     this.grid.getView().focusRow(this.lastActive);
35650                     if(last !== false){
35651                         this.last = last;
35652                     }
35653                 }else{
35654                     this.selectFirstRow();
35655                 }
35656                 this.fireEvent("afterselectionchange", this);
35657             },
35658             scope: this
35659         });
35660
35661         var view = this.grid.view;
35662         view.on("refresh", this.onRefresh, this);
35663         view.on("rowupdated", this.onRowUpdated, this);
35664         view.on("rowremoved", this.onRemove, this);
35665     },
35666
35667     // private
35668     onRefresh : function(){
35669         var ds = this.grid.dataSource, i, v = this.grid.view;
35670         var s = this.selections;
35671         s.each(function(r){
35672             if((i = ds.indexOfId(r.id)) != -1){
35673                 v.onRowSelect(i);
35674                 s.add(ds.getAt(i)); // updating the selection relate data
35675             }else{
35676                 s.remove(r);
35677             }
35678         });
35679     },
35680
35681     // private
35682     onRemove : function(v, index, r){
35683         this.selections.remove(r);
35684     },
35685
35686     // private
35687     onRowUpdated : function(v, index, r){
35688         if(this.isSelected(r)){
35689             v.onRowSelect(index);
35690         }
35691     },
35692
35693     /**
35694      * Select records.
35695      * @param {Array} records The records to select
35696      * @param {Boolean} keepExisting (optional) True to keep existing selections
35697      */
35698     selectRecords : function(records, keepExisting){
35699         if(!keepExisting){
35700             this.clearSelections();
35701         }
35702         var ds = this.grid.dataSource;
35703         for(var i = 0, len = records.length; i < len; i++){
35704             this.selectRow(ds.indexOf(records[i]), true);
35705         }
35706     },
35707
35708     /**
35709      * Gets the number of selected rows.
35710      * @return {Number}
35711      */
35712     getCount : function(){
35713         return this.selections.length;
35714     },
35715
35716     /**
35717      * Selects the first row in the grid.
35718      */
35719     selectFirstRow : function(){
35720         this.selectRow(0);
35721     },
35722
35723     /**
35724      * Select the last row.
35725      * @param {Boolean} keepExisting (optional) True to keep existing selections
35726      */
35727     selectLastRow : function(keepExisting){
35728         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35729     },
35730
35731     /**
35732      * Selects the row immediately following the last selected row.
35733      * @param {Boolean} keepExisting (optional) True to keep existing selections
35734      */
35735     selectNext : function(keepExisting){
35736         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35737             this.selectRow(this.last+1, keepExisting);
35738             this.grid.getView().focusRow(this.last);
35739         }
35740     },
35741
35742     /**
35743      * Selects the row that precedes the last selected row.
35744      * @param {Boolean} keepExisting (optional) True to keep existing selections
35745      */
35746     selectPrevious : function(keepExisting){
35747         if(this.last){
35748             this.selectRow(this.last-1, keepExisting);
35749             this.grid.getView().focusRow(this.last);
35750         }
35751     },
35752
35753     /**
35754      * Returns the selected records
35755      * @return {Array} Array of selected records
35756      */
35757     getSelections : function(){
35758         return [].concat(this.selections.items);
35759     },
35760
35761     /**
35762      * Returns the first selected record.
35763      * @return {Record}
35764      */
35765     getSelected : function(){
35766         return this.selections.itemAt(0);
35767     },
35768
35769
35770     /**
35771      * Clears all selections.
35772      */
35773     clearSelections : function(fast){
35774         if(this.locked) {
35775             return;
35776         }
35777         if(fast !== true){
35778             var ds = this.grid.dataSource;
35779             var s = this.selections;
35780             s.each(function(r){
35781                 this.deselectRow(ds.indexOfId(r.id));
35782             }, this);
35783             s.clear();
35784         }else{
35785             this.selections.clear();
35786         }
35787         this.last = false;
35788     },
35789
35790
35791     /**
35792      * Selects all rows.
35793      */
35794     selectAll : function(){
35795         if(this.locked) {
35796             return;
35797         }
35798         this.selections.clear();
35799         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35800             this.selectRow(i, true);
35801         }
35802     },
35803
35804     /**
35805      * Returns True if there is a selection.
35806      * @return {Boolean}
35807      */
35808     hasSelection : function(){
35809         return this.selections.length > 0;
35810     },
35811
35812     /**
35813      * Returns True if the specified row is selected.
35814      * @param {Number/Record} record The record or index of the record to check
35815      * @return {Boolean}
35816      */
35817     isSelected : function(index){
35818         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35819         return (r && this.selections.key(r.id) ? true : false);
35820     },
35821
35822     /**
35823      * Returns True if the specified record id is selected.
35824      * @param {String} id The id of record to check
35825      * @return {Boolean}
35826      */
35827     isIdSelected : function(id){
35828         return (this.selections.key(id) ? true : false);
35829     },
35830
35831     // private
35832     handleMouseDown : function(e, t){
35833         var view = this.grid.getView(), rowIndex;
35834         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35835             return;
35836         };
35837         if(e.shiftKey && this.last !== false){
35838             var last = this.last;
35839             this.selectRange(last, rowIndex, e.ctrlKey);
35840             this.last = last; // reset the last
35841             view.focusRow(rowIndex);
35842         }else{
35843             var isSelected = this.isSelected(rowIndex);
35844             if(e.button !== 0 && isSelected){
35845                 view.focusRow(rowIndex);
35846             }else if(e.ctrlKey && isSelected){
35847                 this.deselectRow(rowIndex);
35848             }else if(!isSelected){
35849                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35850                 view.focusRow(rowIndex);
35851             }
35852         }
35853         this.fireEvent("afterselectionchange", this);
35854     },
35855     // private
35856     handleDragableRowClick :  function(grid, rowIndex, e) 
35857     {
35858         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35859             this.selectRow(rowIndex, false);
35860             grid.view.focusRow(rowIndex);
35861              this.fireEvent("afterselectionchange", this);
35862         }
35863     },
35864     
35865     /**
35866      * Selects multiple rows.
35867      * @param {Array} rows Array of the indexes of the row to select
35868      * @param {Boolean} keepExisting (optional) True to keep existing selections
35869      */
35870     selectRows : function(rows, keepExisting){
35871         if(!keepExisting){
35872             this.clearSelections();
35873         }
35874         for(var i = 0, len = rows.length; i < len; i++){
35875             this.selectRow(rows[i], true);
35876         }
35877     },
35878
35879     /**
35880      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35881      * @param {Number} startRow The index of the first row in the range
35882      * @param {Number} endRow The index of the last row in the range
35883      * @param {Boolean} keepExisting (optional) True to retain existing selections
35884      */
35885     selectRange : function(startRow, endRow, keepExisting){
35886         if(this.locked) {
35887             return;
35888         }
35889         if(!keepExisting){
35890             this.clearSelections();
35891         }
35892         if(startRow <= endRow){
35893             for(var i = startRow; i <= endRow; i++){
35894                 this.selectRow(i, true);
35895             }
35896         }else{
35897             for(var i = startRow; i >= endRow; i--){
35898                 this.selectRow(i, true);
35899             }
35900         }
35901     },
35902
35903     /**
35904      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35905      * @param {Number} startRow The index of the first row in the range
35906      * @param {Number} endRow The index of the last row in the range
35907      */
35908     deselectRange : function(startRow, endRow, preventViewNotify){
35909         if(this.locked) {
35910             return;
35911         }
35912         for(var i = startRow; i <= endRow; i++){
35913             this.deselectRow(i, preventViewNotify);
35914         }
35915     },
35916
35917     /**
35918      * Selects a row.
35919      * @param {Number} row The index of the row to select
35920      * @param {Boolean} keepExisting (optional) True to keep existing selections
35921      */
35922     selectRow : function(index, keepExisting, preventViewNotify){
35923         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
35924             return;
35925         }
35926         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35927             if(!keepExisting || this.singleSelect){
35928                 this.clearSelections();
35929             }
35930             var r = this.grid.dataSource.getAt(index);
35931             this.selections.add(r);
35932             this.last = this.lastActive = index;
35933             if(!preventViewNotify){
35934                 this.grid.getView().onRowSelect(index);
35935             }
35936             this.fireEvent("rowselect", this, index, r);
35937             this.fireEvent("selectionchange", this);
35938         }
35939     },
35940
35941     /**
35942      * Deselects a row.
35943      * @param {Number} row The index of the row to deselect
35944      */
35945     deselectRow : function(index, preventViewNotify){
35946         if(this.locked) {
35947             return;
35948         }
35949         if(this.last == index){
35950             this.last = false;
35951         }
35952         if(this.lastActive == index){
35953             this.lastActive = false;
35954         }
35955         var r = this.grid.dataSource.getAt(index);
35956         this.selections.remove(r);
35957         if(!preventViewNotify){
35958             this.grid.getView().onRowDeselect(index);
35959         }
35960         this.fireEvent("rowdeselect", this, index);
35961         this.fireEvent("selectionchange", this);
35962     },
35963
35964     // private
35965     restoreLast : function(){
35966         if(this._last){
35967             this.last = this._last;
35968         }
35969     },
35970
35971     // private
35972     acceptsNav : function(row, col, cm){
35973         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35974     },
35975
35976     // private
35977     onEditorKey : function(field, e){
35978         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35979         if(k == e.TAB){
35980             e.stopEvent();
35981             ed.completeEdit();
35982             if(e.shiftKey){
35983                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35984             }else{
35985                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35986             }
35987         }else if(k == e.ENTER && !e.ctrlKey){
35988             e.stopEvent();
35989             ed.completeEdit();
35990             if(e.shiftKey){
35991                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35992             }else{
35993                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35994             }
35995         }else if(k == e.ESC){
35996             ed.cancelEdit();
35997         }
35998         if(newCell){
35999             g.startEditing(newCell[0], newCell[1]);
36000         }
36001     }
36002 });/*
36003  * Based on:
36004  * Ext JS Library 1.1.1
36005  * Copyright(c) 2006-2007, Ext JS, LLC.
36006  *
36007  * Originally Released Under LGPL - original licence link has changed is not relivant.
36008  *
36009  * Fork - LGPL
36010  * <script type="text/javascript">
36011  */
36012 /**
36013  * @class Roo.grid.CellSelectionModel
36014  * @extends Roo.grid.AbstractSelectionModel
36015  * This class provides the basic implementation for cell selection in a grid.
36016  * @constructor
36017  * @param {Object} config The object containing the configuration of this model.
36018  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36019  */
36020 Roo.grid.CellSelectionModel = function(config){
36021     Roo.apply(this, config);
36022
36023     this.selection = null;
36024
36025     this.addEvents({
36026         /**
36027              * @event beforerowselect
36028              * Fires before a cell is selected.
36029              * @param {SelectionModel} this
36030              * @param {Number} rowIndex The selected row index
36031              * @param {Number} colIndex The selected cell index
36032              */
36033             "beforecellselect" : true,
36034         /**
36035              * @event cellselect
36036              * Fires when a cell is selected.
36037              * @param {SelectionModel} this
36038              * @param {Number} rowIndex The selected row index
36039              * @param {Number} colIndex The selected cell index
36040              */
36041             "cellselect" : true,
36042         /**
36043              * @event selectionchange
36044              * Fires when the active selection changes.
36045              * @param {SelectionModel} this
36046              * @param {Object} selection null for no selection or an object (o) with two properties
36047                 <ul>
36048                 <li>o.record: the record object for the row the selection is in</li>
36049                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36050                 </ul>
36051              */
36052             "selectionchange" : true,
36053         /**
36054              * @event tabend
36055              * Fires when the tab (or enter) was pressed on the last editable cell
36056              * You can use this to trigger add new row.
36057              * @param {SelectionModel} this
36058              */
36059             "tabend" : true,
36060          /**
36061              * @event beforeeditnext
36062              * Fires before the next editable sell is made active
36063              * You can use this to skip to another cell or fire the tabend
36064              *    if you set cell to false
36065              * @param {Object} eventdata object : { cell : [ row, col ] } 
36066              */
36067             "beforeeditnext" : true
36068     });
36069     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36070 };
36071
36072 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36073     
36074     enter_is_tab: false,
36075
36076     /** @ignore */
36077     initEvents : function(){
36078         this.grid.on("mousedown", this.handleMouseDown, this);
36079         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36080         var view = this.grid.view;
36081         view.on("refresh", this.onViewChange, this);
36082         view.on("rowupdated", this.onRowUpdated, this);
36083         view.on("beforerowremoved", this.clearSelections, this);
36084         view.on("beforerowsinserted", this.clearSelections, this);
36085         if(this.grid.isEditor){
36086             this.grid.on("beforeedit", this.beforeEdit,  this);
36087         }
36088     },
36089
36090         //private
36091     beforeEdit : function(e){
36092         this.select(e.row, e.column, false, true, e.record);
36093     },
36094
36095         //private
36096     onRowUpdated : function(v, index, r){
36097         if(this.selection && this.selection.record == r){
36098             v.onCellSelect(index, this.selection.cell[1]);
36099         }
36100     },
36101
36102         //private
36103     onViewChange : function(){
36104         this.clearSelections(true);
36105     },
36106
36107         /**
36108          * Returns the currently selected cell,.
36109          * @return {Array} The selected cell (row, column) or null if none selected.
36110          */
36111     getSelectedCell : function(){
36112         return this.selection ? this.selection.cell : null;
36113     },
36114
36115     /**
36116      * Clears all selections.
36117      * @param {Boolean} true to prevent the gridview from being notified about the change.
36118      */
36119     clearSelections : function(preventNotify){
36120         var s = this.selection;
36121         if(s){
36122             if(preventNotify !== true){
36123                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36124             }
36125             this.selection = null;
36126             this.fireEvent("selectionchange", this, null);
36127         }
36128     },
36129
36130     /**
36131      * Returns true if there is a selection.
36132      * @return {Boolean}
36133      */
36134     hasSelection : function(){
36135         return this.selection ? true : false;
36136     },
36137
36138     /** @ignore */
36139     handleMouseDown : function(e, t){
36140         var v = this.grid.getView();
36141         if(this.isLocked()){
36142             return;
36143         };
36144         var row = v.findRowIndex(t);
36145         var cell = v.findCellIndex(t);
36146         if(row !== false && cell !== false){
36147             this.select(row, cell);
36148         }
36149     },
36150
36151     /**
36152      * Selects a cell.
36153      * @param {Number} rowIndex
36154      * @param {Number} collIndex
36155      */
36156     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36157         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36158             this.clearSelections();
36159             r = r || this.grid.dataSource.getAt(rowIndex);
36160             this.selection = {
36161                 record : r,
36162                 cell : [rowIndex, colIndex]
36163             };
36164             if(!preventViewNotify){
36165                 var v = this.grid.getView();
36166                 v.onCellSelect(rowIndex, colIndex);
36167                 if(preventFocus !== true){
36168                     v.focusCell(rowIndex, colIndex);
36169                 }
36170             }
36171             this.fireEvent("cellselect", this, rowIndex, colIndex);
36172             this.fireEvent("selectionchange", this, this.selection);
36173         }
36174     },
36175
36176         //private
36177     isSelectable : function(rowIndex, colIndex, cm){
36178         return !cm.isHidden(colIndex);
36179     },
36180
36181     /** @ignore */
36182     handleKeyDown : function(e){
36183         //Roo.log('Cell Sel Model handleKeyDown');
36184         if(!e.isNavKeyPress()){
36185             return;
36186         }
36187         var g = this.grid, s = this.selection;
36188         if(!s){
36189             e.stopEvent();
36190             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36191             if(cell){
36192                 this.select(cell[0], cell[1]);
36193             }
36194             return;
36195         }
36196         var sm = this;
36197         var walk = function(row, col, step){
36198             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36199         };
36200         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36201         var newCell;
36202
36203       
36204
36205         switch(k){
36206             case e.TAB:
36207                 // handled by onEditorKey
36208                 if (g.isEditor && g.editing) {
36209                     return;
36210                 }
36211                 if(e.shiftKey) {
36212                     newCell = walk(r, c-1, -1);
36213                 } else {
36214                     newCell = walk(r, c+1, 1);
36215                 }
36216                 break;
36217             
36218             case e.DOWN:
36219                newCell = walk(r+1, c, 1);
36220                 break;
36221             
36222             case e.UP:
36223                 newCell = walk(r-1, c, -1);
36224                 break;
36225             
36226             case e.RIGHT:
36227                 newCell = walk(r, c+1, 1);
36228                 break;
36229             
36230             case e.LEFT:
36231                 newCell = walk(r, c-1, -1);
36232                 break;
36233             
36234             case e.ENTER:
36235                 
36236                 if(g.isEditor && !g.editing){
36237                    g.startEditing(r, c);
36238                    e.stopEvent();
36239                    return;
36240                 }
36241                 
36242                 
36243              break;
36244         };
36245         if(newCell){
36246             this.select(newCell[0], newCell[1]);
36247             e.stopEvent();
36248             
36249         }
36250     },
36251
36252     acceptsNav : function(row, col, cm){
36253         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36254     },
36255     /**
36256      * Selects a cell.
36257      * @param {Number} field (not used) - as it's normally used as a listener
36258      * @param {Number} e - event - fake it by using
36259      *
36260      * var e = Roo.EventObjectImpl.prototype;
36261      * e.keyCode = e.TAB
36262      *
36263      * 
36264      */
36265     onEditorKey : function(field, e){
36266         
36267         var k = e.getKey(),
36268             newCell,
36269             g = this.grid,
36270             ed = g.activeEditor,
36271             forward = false;
36272         ///Roo.log('onEditorKey' + k);
36273         
36274         
36275         if (this.enter_is_tab && k == e.ENTER) {
36276             k = e.TAB;
36277         }
36278         
36279         if(k == e.TAB){
36280             if(e.shiftKey){
36281                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36282             }else{
36283                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36284                 forward = true;
36285             }
36286             
36287             e.stopEvent();
36288             
36289         } else if(k == e.ENTER &&  !e.ctrlKey){
36290             ed.completeEdit();
36291             e.stopEvent();
36292             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36293         
36294                 } else if(k == e.ESC){
36295             ed.cancelEdit();
36296         }
36297                 
36298         if (newCell) {
36299             var ecall = { cell : newCell, forward : forward };
36300             this.fireEvent('beforeeditnext', ecall );
36301             newCell = ecall.cell;
36302                         forward = ecall.forward;
36303         }
36304                 
36305         if(newCell){
36306             //Roo.log('next cell after edit');
36307             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36308         } else if (forward) {
36309             // tabbed past last
36310             this.fireEvent.defer(100, this, ['tabend',this]);
36311         }
36312     }
36313 });/*
36314  * Based on:
36315  * Ext JS Library 1.1.1
36316  * Copyright(c) 2006-2007, Ext JS, LLC.
36317  *
36318  * Originally Released Under LGPL - original licence link has changed is not relivant.
36319  *
36320  * Fork - LGPL
36321  * <script type="text/javascript">
36322  */
36323  
36324 /**
36325  * @class Roo.grid.EditorGrid
36326  * @extends Roo.grid.Grid
36327  * Class for creating and editable grid.
36328  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36329  * The container MUST have some type of size defined for the grid to fill. The container will be 
36330  * automatically set to position relative if it isn't already.
36331  * @param {Object} dataSource The data model to bind to
36332  * @param {Object} colModel The column model with info about this grid's columns
36333  */
36334 Roo.grid.EditorGrid = function(container, config){
36335     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36336     this.getGridEl().addClass("xedit-grid");
36337
36338     if(!this.selModel){
36339         this.selModel = new Roo.grid.CellSelectionModel();
36340     }
36341
36342     this.activeEditor = null;
36343
36344         this.addEvents({
36345             /**
36346              * @event beforeedit
36347              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36348              * <ul style="padding:5px;padding-left:16px;">
36349              * <li>grid - This grid</li>
36350              * <li>record - The record being edited</li>
36351              * <li>field - The field name being edited</li>
36352              * <li>value - The value for the field being edited.</li>
36353              * <li>row - The grid row index</li>
36354              * <li>column - The grid column index</li>
36355              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36356              * </ul>
36357              * @param {Object} e An edit event (see above for description)
36358              */
36359             "beforeedit" : true,
36360             /**
36361              * @event afteredit
36362              * Fires after a cell is edited. <br />
36363              * <ul style="padding:5px;padding-left:16px;">
36364              * <li>grid - This grid</li>
36365              * <li>record - The record being edited</li>
36366              * <li>field - The field name being edited</li>
36367              * <li>value - The value being set</li>
36368              * <li>originalValue - The original value for the field, before the edit.</li>
36369              * <li>row - The grid row index</li>
36370              * <li>column - The grid column index</li>
36371              * </ul>
36372              * @param {Object} e An edit event (see above for description)
36373              */
36374             "afteredit" : true,
36375             /**
36376              * @event validateedit
36377              * Fires after a cell is edited, but before the value is set in the record. 
36378          * You can use this to modify the value being set in the field, Return false
36379              * to cancel the change. The edit event object has the following properties <br />
36380              * <ul style="padding:5px;padding-left:16px;">
36381          * <li>editor - This editor</li>
36382              * <li>grid - This grid</li>
36383              * <li>record - The record being edited</li>
36384              * <li>field - The field name being edited</li>
36385              * <li>value - The value being set</li>
36386              * <li>originalValue - The original value for the field, before the edit.</li>
36387              * <li>row - The grid row index</li>
36388              * <li>column - The grid column index</li>
36389              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36390              * </ul>
36391              * @param {Object} e An edit event (see above for description)
36392              */
36393             "validateedit" : true
36394         });
36395     this.on("bodyscroll", this.stopEditing,  this);
36396     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36397 };
36398
36399 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36400     /**
36401      * @cfg {Number} clicksToEdit
36402      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36403      */
36404     clicksToEdit: 2,
36405
36406     // private
36407     isEditor : true,
36408     // private
36409     trackMouseOver: false, // causes very odd FF errors
36410
36411     onCellDblClick : function(g, row, col){
36412         this.startEditing(row, col);
36413     },
36414
36415     onEditComplete : function(ed, value, startValue){
36416         this.editing = false;
36417         this.activeEditor = null;
36418         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36419         var r = ed.record;
36420         var field = this.colModel.getDataIndex(ed.col);
36421         var e = {
36422             grid: this,
36423             record: r,
36424             field: field,
36425             originalValue: startValue,
36426             value: value,
36427             row: ed.row,
36428             column: ed.col,
36429             cancel:false,
36430             editor: ed
36431         };
36432         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36433         cell.show();
36434           
36435         if(String(value) !== String(startValue)){
36436             
36437             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36438                 r.set(field, e.value);
36439                 // if we are dealing with a combo box..
36440                 // then we also set the 'name' colum to be the displayField
36441                 if (ed.field.displayField && ed.field.name) {
36442                     r.set(ed.field.name, ed.field.el.dom.value);
36443                 }
36444                 
36445                 delete e.cancel; //?? why!!!
36446                 this.fireEvent("afteredit", e);
36447             }
36448         } else {
36449             this.fireEvent("afteredit", e); // always fire it!
36450         }
36451         this.view.focusCell(ed.row, ed.col);
36452     },
36453
36454     /**
36455      * Starts editing the specified for the specified row/column
36456      * @param {Number} rowIndex
36457      * @param {Number} colIndex
36458      */
36459     startEditing : function(row, col){
36460         this.stopEditing();
36461         if(this.colModel.isCellEditable(col, row)){
36462             this.view.ensureVisible(row, col, true);
36463           
36464             var r = this.dataSource.getAt(row);
36465             var field = this.colModel.getDataIndex(col);
36466             var cell = Roo.get(this.view.getCell(row,col));
36467             var e = {
36468                 grid: this,
36469                 record: r,
36470                 field: field,
36471                 value: r.data[field],
36472                 row: row,
36473                 column: col,
36474                 cancel:false 
36475             };
36476             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36477                 this.editing = true;
36478                 var ed = this.colModel.getCellEditor(col, row);
36479                 
36480                 if (!ed) {
36481                     return;
36482                 }
36483                 if(!ed.rendered){
36484                     ed.render(ed.parentEl || document.body);
36485                 }
36486                 ed.field.reset();
36487                
36488                 cell.hide();
36489                 
36490                 (function(){ // complex but required for focus issues in safari, ie and opera
36491                     ed.row = row;
36492                     ed.col = col;
36493                     ed.record = r;
36494                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36495                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36496                     this.activeEditor = ed;
36497                     var v = r.data[field];
36498                     ed.startEdit(this.view.getCell(row, col), v);
36499                     // combo's with 'displayField and name set
36500                     if (ed.field.displayField && ed.field.name) {
36501                         ed.field.el.dom.value = r.data[ed.field.name];
36502                     }
36503                     
36504                     
36505                 }).defer(50, this);
36506             }
36507         }
36508     },
36509         
36510     /**
36511      * Stops any active editing
36512      */
36513     stopEditing : function(){
36514         if(this.activeEditor){
36515             this.activeEditor.completeEdit();
36516         }
36517         this.activeEditor = null;
36518     },
36519         
36520          /**
36521      * Called to get grid's drag proxy text, by default returns this.ddText.
36522      * @return {String}
36523      */
36524     getDragDropText : function(){
36525         var count = this.selModel.getSelectedCell() ? 1 : 0;
36526         return String.format(this.ddText, count, count == 1 ? '' : 's');
36527     }
36528         
36529 });/*
36530  * Based on:
36531  * Ext JS Library 1.1.1
36532  * Copyright(c) 2006-2007, Ext JS, LLC.
36533  *
36534  * Originally Released Under LGPL - original licence link has changed is not relivant.
36535  *
36536  * Fork - LGPL
36537  * <script type="text/javascript">
36538  */
36539
36540 // private - not really -- you end up using it !
36541 // This is a support class used internally by the Grid components
36542
36543 /**
36544  * @class Roo.grid.GridEditor
36545  * @extends Roo.Editor
36546  * Class for creating and editable grid elements.
36547  * @param {Object} config any settings (must include field)
36548  */
36549 Roo.grid.GridEditor = function(field, config){
36550     if (!config && field.field) {
36551         config = field;
36552         field = Roo.factory(config.field, Roo.form);
36553     }
36554     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36555     field.monitorTab = false;
36556 };
36557
36558 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36559     
36560     /**
36561      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36562      */
36563     
36564     alignment: "tl-tl",
36565     autoSize: "width",
36566     hideEl : false,
36567     cls: "x-small-editor x-grid-editor",
36568     shim:false,
36569     shadow:"frame"
36570 });/*
36571  * Based on:
36572  * Ext JS Library 1.1.1
36573  * Copyright(c) 2006-2007, Ext JS, LLC.
36574  *
36575  * Originally Released Under LGPL - original licence link has changed is not relivant.
36576  *
36577  * Fork - LGPL
36578  * <script type="text/javascript">
36579  */
36580   
36581
36582   
36583 Roo.grid.PropertyRecord = Roo.data.Record.create([
36584     {name:'name',type:'string'},  'value'
36585 ]);
36586
36587
36588 Roo.grid.PropertyStore = function(grid, source){
36589     this.grid = grid;
36590     this.store = new Roo.data.Store({
36591         recordType : Roo.grid.PropertyRecord
36592     });
36593     this.store.on('update', this.onUpdate,  this);
36594     if(source){
36595         this.setSource(source);
36596     }
36597     Roo.grid.PropertyStore.superclass.constructor.call(this);
36598 };
36599
36600
36601
36602 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36603     setSource : function(o){
36604         this.source = o;
36605         this.store.removeAll();
36606         var data = [];
36607         for(var k in o){
36608             if(this.isEditableValue(o[k])){
36609                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36610             }
36611         }
36612         this.store.loadRecords({records: data}, {}, true);
36613     },
36614
36615     onUpdate : function(ds, record, type){
36616         if(type == Roo.data.Record.EDIT){
36617             var v = record.data['value'];
36618             var oldValue = record.modified['value'];
36619             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36620                 this.source[record.id] = v;
36621                 record.commit();
36622                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36623             }else{
36624                 record.reject();
36625             }
36626         }
36627     },
36628
36629     getProperty : function(row){
36630        return this.store.getAt(row);
36631     },
36632
36633     isEditableValue: function(val){
36634         if(val && val instanceof Date){
36635             return true;
36636         }else if(typeof val == 'object' || typeof val == 'function'){
36637             return false;
36638         }
36639         return true;
36640     },
36641
36642     setValue : function(prop, value){
36643         this.source[prop] = value;
36644         this.store.getById(prop).set('value', value);
36645     },
36646
36647     getSource : function(){
36648         return this.source;
36649     }
36650 });
36651
36652 Roo.grid.PropertyColumnModel = function(grid, store){
36653     this.grid = grid;
36654     var g = Roo.grid;
36655     g.PropertyColumnModel.superclass.constructor.call(this, [
36656         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36657         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36658     ]);
36659     this.store = store;
36660     this.bselect = Roo.DomHelper.append(document.body, {
36661         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36662             {tag: 'option', value: 'true', html: 'true'},
36663             {tag: 'option', value: 'false', html: 'false'}
36664         ]
36665     });
36666     Roo.id(this.bselect);
36667     var f = Roo.form;
36668     this.editors = {
36669         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36670         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36671         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36672         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36673         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36674     };
36675     this.renderCellDelegate = this.renderCell.createDelegate(this);
36676     this.renderPropDelegate = this.renderProp.createDelegate(this);
36677 };
36678
36679 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36680     
36681     
36682     nameText : 'Name',
36683     valueText : 'Value',
36684     
36685     dateFormat : 'm/j/Y',
36686     
36687     
36688     renderDate : function(dateVal){
36689         return dateVal.dateFormat(this.dateFormat);
36690     },
36691
36692     renderBool : function(bVal){
36693         return bVal ? 'true' : 'false';
36694     },
36695
36696     isCellEditable : function(colIndex, rowIndex){
36697         return colIndex == 1;
36698     },
36699
36700     getRenderer : function(col){
36701         return col == 1 ?
36702             this.renderCellDelegate : this.renderPropDelegate;
36703     },
36704
36705     renderProp : function(v){
36706         return this.getPropertyName(v);
36707     },
36708
36709     renderCell : function(val){
36710         var rv = val;
36711         if(val instanceof Date){
36712             rv = this.renderDate(val);
36713         }else if(typeof val == 'boolean'){
36714             rv = this.renderBool(val);
36715         }
36716         return Roo.util.Format.htmlEncode(rv);
36717     },
36718
36719     getPropertyName : function(name){
36720         var pn = this.grid.propertyNames;
36721         return pn && pn[name] ? pn[name] : name;
36722     },
36723
36724     getCellEditor : function(colIndex, rowIndex){
36725         var p = this.store.getProperty(rowIndex);
36726         var n = p.data['name'], val = p.data['value'];
36727         
36728         if(typeof(this.grid.customEditors[n]) == 'string'){
36729             return this.editors[this.grid.customEditors[n]];
36730         }
36731         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36732             return this.grid.customEditors[n];
36733         }
36734         if(val instanceof Date){
36735             return this.editors['date'];
36736         }else if(typeof val == 'number'){
36737             return this.editors['number'];
36738         }else if(typeof val == 'boolean'){
36739             return this.editors['boolean'];
36740         }else{
36741             return this.editors['string'];
36742         }
36743     }
36744 });
36745
36746 /**
36747  * @class Roo.grid.PropertyGrid
36748  * @extends Roo.grid.EditorGrid
36749  * This class represents the  interface of a component based property grid control.
36750  * <br><br>Usage:<pre><code>
36751  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36752       
36753  });
36754  // set any options
36755  grid.render();
36756  * </code></pre>
36757   
36758  * @constructor
36759  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36760  * The container MUST have some type of size defined for the grid to fill. The container will be
36761  * automatically set to position relative if it isn't already.
36762  * @param {Object} config A config object that sets properties on this grid.
36763  */
36764 Roo.grid.PropertyGrid = function(container, config){
36765     config = config || {};
36766     var store = new Roo.grid.PropertyStore(this);
36767     this.store = store;
36768     var cm = new Roo.grid.PropertyColumnModel(this, store);
36769     store.store.sort('name', 'ASC');
36770     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36771         ds: store.store,
36772         cm: cm,
36773         enableColLock:false,
36774         enableColumnMove:false,
36775         stripeRows:false,
36776         trackMouseOver: false,
36777         clicksToEdit:1
36778     }, config));
36779     this.getGridEl().addClass('x-props-grid');
36780     this.lastEditRow = null;
36781     this.on('columnresize', this.onColumnResize, this);
36782     this.addEvents({
36783          /**
36784              * @event beforepropertychange
36785              * Fires before a property changes (return false to stop?)
36786              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36787              * @param {String} id Record Id
36788              * @param {String} newval New Value
36789          * @param {String} oldval Old Value
36790              */
36791         "beforepropertychange": true,
36792         /**
36793              * @event propertychange
36794              * Fires after a property changes
36795              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36796              * @param {String} id Record Id
36797              * @param {String} newval New Value
36798          * @param {String} oldval Old Value
36799              */
36800         "propertychange": true
36801     });
36802     this.customEditors = this.customEditors || {};
36803 };
36804 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36805     
36806      /**
36807      * @cfg {Object} customEditors map of colnames=> custom editors.
36808      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36809      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36810      * false disables editing of the field.
36811          */
36812     
36813       /**
36814      * @cfg {Object} propertyNames map of property Names to their displayed value
36815          */
36816     
36817     render : function(){
36818         Roo.grid.PropertyGrid.superclass.render.call(this);
36819         this.autoSize.defer(100, this);
36820     },
36821
36822     autoSize : function(){
36823         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36824         if(this.view){
36825             this.view.fitColumns();
36826         }
36827     },
36828
36829     onColumnResize : function(){
36830         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36831         this.autoSize();
36832     },
36833     /**
36834      * Sets the data for the Grid
36835      * accepts a Key => Value object of all the elements avaiable.
36836      * @param {Object} data  to appear in grid.
36837      */
36838     setSource : function(source){
36839         this.store.setSource(source);
36840         //this.autoSize();
36841     },
36842     /**
36843      * Gets all the data from the grid.
36844      * @return {Object} data  data stored in grid
36845      */
36846     getSource : function(){
36847         return this.store.getSource();
36848     }
36849 });/*
36850   
36851  * Licence LGPL
36852  
36853  */
36854  
36855 /**
36856  * @class Roo.grid.Calendar
36857  * @extends Roo.util.Grid
36858  * This class extends the Grid to provide a calendar widget
36859  * <br><br>Usage:<pre><code>
36860  var grid = new Roo.grid.Calendar("my-container-id", {
36861      ds: myDataStore,
36862      cm: myColModel,
36863      selModel: mySelectionModel,
36864      autoSizeColumns: true,
36865      monitorWindowResize: false,
36866      trackMouseOver: true
36867      eventstore : real data store..
36868  });
36869  // set any options
36870  grid.render();
36871   
36872   * @constructor
36873  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36874  * The container MUST have some type of size defined for the grid to fill. The container will be
36875  * automatically set to position relative if it isn't already.
36876  * @param {Object} config A config object that sets properties on this grid.
36877  */
36878 Roo.grid.Calendar = function(container, config){
36879         // initialize the container
36880         this.container = Roo.get(container);
36881         this.container.update("");
36882         this.container.setStyle("overflow", "hidden");
36883     this.container.addClass('x-grid-container');
36884
36885     this.id = this.container.id;
36886
36887     Roo.apply(this, config);
36888     // check and correct shorthanded configs
36889     
36890     var rows = [];
36891     var d =1;
36892     for (var r = 0;r < 6;r++) {
36893         
36894         rows[r]=[];
36895         for (var c =0;c < 7;c++) {
36896             rows[r][c]= '';
36897         }
36898     }
36899     if (this.eventStore) {
36900         this.eventStore= Roo.factory(this.eventStore, Roo.data);
36901         this.eventStore.on('load',this.onLoad, this);
36902         this.eventStore.on('beforeload',this.clearEvents, this);
36903          
36904     }
36905     
36906     this.dataSource = new Roo.data.Store({
36907             proxy: new Roo.data.MemoryProxy(rows),
36908             reader: new Roo.data.ArrayReader({}, [
36909                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
36910     });
36911
36912     this.dataSource.load();
36913     this.ds = this.dataSource;
36914     this.ds.xmodule = this.xmodule || false;
36915     
36916     
36917     var cellRender = function(v,x,r)
36918     {
36919         return String.format(
36920             '<div class="fc-day  fc-widget-content"><div>' +
36921                 '<div class="fc-event-container"></div>' +
36922                 '<div class="fc-day-number">{0}</div>'+
36923                 
36924                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
36925             '</div></div>', v);
36926     
36927     }
36928     
36929     
36930     this.colModel = new Roo.grid.ColumnModel( [
36931         {
36932             xtype: 'ColumnModel',
36933             xns: Roo.grid,
36934             dataIndex : 'weekday0',
36935             header : 'Sunday',
36936             renderer : cellRender
36937         },
36938         {
36939             xtype: 'ColumnModel',
36940             xns: Roo.grid,
36941             dataIndex : 'weekday1',
36942             header : 'Monday',
36943             renderer : cellRender
36944         },
36945         {
36946             xtype: 'ColumnModel',
36947             xns: Roo.grid,
36948             dataIndex : 'weekday2',
36949             header : 'Tuesday',
36950             renderer : cellRender
36951         },
36952         {
36953             xtype: 'ColumnModel',
36954             xns: Roo.grid,
36955             dataIndex : 'weekday3',
36956             header : 'Wednesday',
36957             renderer : cellRender
36958         },
36959         {
36960             xtype: 'ColumnModel',
36961             xns: Roo.grid,
36962             dataIndex : 'weekday4',
36963             header : 'Thursday',
36964             renderer : cellRender
36965         },
36966         {
36967             xtype: 'ColumnModel',
36968             xns: Roo.grid,
36969             dataIndex : 'weekday5',
36970             header : 'Friday',
36971             renderer : cellRender
36972         },
36973         {
36974             xtype: 'ColumnModel',
36975             xns: Roo.grid,
36976             dataIndex : 'weekday6',
36977             header : 'Saturday',
36978             renderer : cellRender
36979         }
36980     ]);
36981     this.cm = this.colModel;
36982     this.cm.xmodule = this.xmodule || false;
36983  
36984         
36985           
36986     //this.selModel = new Roo.grid.CellSelectionModel();
36987     //this.sm = this.selModel;
36988     //this.selModel.init(this);
36989     
36990     
36991     if(this.width){
36992         this.container.setWidth(this.width);
36993     }
36994
36995     if(this.height){
36996         this.container.setHeight(this.height);
36997     }
36998     /** @private */
36999         this.addEvents({
37000         // raw events
37001         /**
37002          * @event click
37003          * The raw click event for the entire grid.
37004          * @param {Roo.EventObject} e
37005          */
37006         "click" : true,
37007         /**
37008          * @event dblclick
37009          * The raw dblclick event for the entire grid.
37010          * @param {Roo.EventObject} e
37011          */
37012         "dblclick" : true,
37013         /**
37014          * @event contextmenu
37015          * The raw contextmenu event for the entire grid.
37016          * @param {Roo.EventObject} e
37017          */
37018         "contextmenu" : true,
37019         /**
37020          * @event mousedown
37021          * The raw mousedown event for the entire grid.
37022          * @param {Roo.EventObject} e
37023          */
37024         "mousedown" : true,
37025         /**
37026          * @event mouseup
37027          * The raw mouseup event for the entire grid.
37028          * @param {Roo.EventObject} e
37029          */
37030         "mouseup" : true,
37031         /**
37032          * @event mouseover
37033          * The raw mouseover event for the entire grid.
37034          * @param {Roo.EventObject} e
37035          */
37036         "mouseover" : true,
37037         /**
37038          * @event mouseout
37039          * The raw mouseout event for the entire grid.
37040          * @param {Roo.EventObject} e
37041          */
37042         "mouseout" : true,
37043         /**
37044          * @event keypress
37045          * The raw keypress event for the entire grid.
37046          * @param {Roo.EventObject} e
37047          */
37048         "keypress" : true,
37049         /**
37050          * @event keydown
37051          * The raw keydown event for the entire grid.
37052          * @param {Roo.EventObject} e
37053          */
37054         "keydown" : true,
37055
37056         // custom events
37057
37058         /**
37059          * @event cellclick
37060          * Fires when a cell is clicked
37061          * @param {Grid} this
37062          * @param {Number} rowIndex
37063          * @param {Number} columnIndex
37064          * @param {Roo.EventObject} e
37065          */
37066         "cellclick" : true,
37067         /**
37068          * @event celldblclick
37069          * Fires when a cell is double clicked
37070          * @param {Grid} this
37071          * @param {Number} rowIndex
37072          * @param {Number} columnIndex
37073          * @param {Roo.EventObject} e
37074          */
37075         "celldblclick" : true,
37076         /**
37077          * @event rowclick
37078          * Fires when a row is clicked
37079          * @param {Grid} this
37080          * @param {Number} rowIndex
37081          * @param {Roo.EventObject} e
37082          */
37083         "rowclick" : true,
37084         /**
37085          * @event rowdblclick
37086          * Fires when a row is double clicked
37087          * @param {Grid} this
37088          * @param {Number} rowIndex
37089          * @param {Roo.EventObject} e
37090          */
37091         "rowdblclick" : true,
37092         /**
37093          * @event headerclick
37094          * Fires when a header is clicked
37095          * @param {Grid} this
37096          * @param {Number} columnIndex
37097          * @param {Roo.EventObject} e
37098          */
37099         "headerclick" : true,
37100         /**
37101          * @event headerdblclick
37102          * Fires when a header cell is double clicked
37103          * @param {Grid} this
37104          * @param {Number} columnIndex
37105          * @param {Roo.EventObject} e
37106          */
37107         "headerdblclick" : true,
37108         /**
37109          * @event rowcontextmenu
37110          * Fires when a row is right clicked
37111          * @param {Grid} this
37112          * @param {Number} rowIndex
37113          * @param {Roo.EventObject} e
37114          */
37115         "rowcontextmenu" : true,
37116         /**
37117          * @event cellcontextmenu
37118          * Fires when a cell is right clicked
37119          * @param {Grid} this
37120          * @param {Number} rowIndex
37121          * @param {Number} cellIndex
37122          * @param {Roo.EventObject} e
37123          */
37124          "cellcontextmenu" : true,
37125         /**
37126          * @event headercontextmenu
37127          * Fires when a header is right clicked
37128          * @param {Grid} this
37129          * @param {Number} columnIndex
37130          * @param {Roo.EventObject} e
37131          */
37132         "headercontextmenu" : true,
37133         /**
37134          * @event bodyscroll
37135          * Fires when the body element is scrolled
37136          * @param {Number} scrollLeft
37137          * @param {Number} scrollTop
37138          */
37139         "bodyscroll" : true,
37140         /**
37141          * @event columnresize
37142          * Fires when the user resizes a column
37143          * @param {Number} columnIndex
37144          * @param {Number} newSize
37145          */
37146         "columnresize" : true,
37147         /**
37148          * @event columnmove
37149          * Fires when the user moves a column
37150          * @param {Number} oldIndex
37151          * @param {Number} newIndex
37152          */
37153         "columnmove" : true,
37154         /**
37155          * @event startdrag
37156          * Fires when row(s) start being dragged
37157          * @param {Grid} this
37158          * @param {Roo.GridDD} dd The drag drop object
37159          * @param {event} e The raw browser event
37160          */
37161         "startdrag" : true,
37162         /**
37163          * @event enddrag
37164          * Fires when a drag operation is complete
37165          * @param {Grid} this
37166          * @param {Roo.GridDD} dd The drag drop object
37167          * @param {event} e The raw browser event
37168          */
37169         "enddrag" : true,
37170         /**
37171          * @event dragdrop
37172          * Fires when dragged row(s) are dropped on a valid DD target
37173          * @param {Grid} this
37174          * @param {Roo.GridDD} dd The drag drop object
37175          * @param {String} targetId The target drag drop object
37176          * @param {event} e The raw browser event
37177          */
37178         "dragdrop" : true,
37179         /**
37180          * @event dragover
37181          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37182          * @param {Grid} this
37183          * @param {Roo.GridDD} dd The drag drop object
37184          * @param {String} targetId The target drag drop object
37185          * @param {event} e The raw browser event
37186          */
37187         "dragover" : true,
37188         /**
37189          * @event dragenter
37190          *  Fires when the dragged row(s) first cross another DD target while being dragged
37191          * @param {Grid} this
37192          * @param {Roo.GridDD} dd The drag drop object
37193          * @param {String} targetId The target drag drop object
37194          * @param {event} e The raw browser event
37195          */
37196         "dragenter" : true,
37197         /**
37198          * @event dragout
37199          * Fires when the dragged row(s) leave another DD target while being dragged
37200          * @param {Grid} this
37201          * @param {Roo.GridDD} dd The drag drop object
37202          * @param {String} targetId The target drag drop object
37203          * @param {event} e The raw browser event
37204          */
37205         "dragout" : true,
37206         /**
37207          * @event rowclass
37208          * Fires when a row is rendered, so you can change add a style to it.
37209          * @param {GridView} gridview   The grid view
37210          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37211          */
37212         'rowclass' : true,
37213
37214         /**
37215          * @event render
37216          * Fires when the grid is rendered
37217          * @param {Grid} grid
37218          */
37219         'render' : true,
37220             /**
37221              * @event select
37222              * Fires when a date is selected
37223              * @param {DatePicker} this
37224              * @param {Date} date The selected date
37225              */
37226         'select': true,
37227         /**
37228              * @event monthchange
37229              * Fires when the displayed month changes 
37230              * @param {DatePicker} this
37231              * @param {Date} date The selected month
37232              */
37233         'monthchange': true,
37234         /**
37235              * @event evententer
37236              * Fires when mouse over an event
37237              * @param {Calendar} this
37238              * @param {event} Event
37239              */
37240         'evententer': true,
37241         /**
37242              * @event eventleave
37243              * Fires when the mouse leaves an
37244              * @param {Calendar} this
37245              * @param {event}
37246              */
37247         'eventleave': true,
37248         /**
37249              * @event eventclick
37250              * Fires when the mouse click an
37251              * @param {Calendar} this
37252              * @param {event}
37253              */
37254         'eventclick': true,
37255         /**
37256              * @event eventrender
37257              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37258              * @param {Calendar} this
37259              * @param {data} data to be modified
37260              */
37261         'eventrender': true
37262         
37263     });
37264
37265     Roo.grid.Grid.superclass.constructor.call(this);
37266     this.on('render', function() {
37267         this.view.el.addClass('x-grid-cal'); 
37268         
37269         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37270
37271     },this);
37272     
37273     if (!Roo.grid.Calendar.style) {
37274         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37275             
37276             
37277             '.x-grid-cal .x-grid-col' :  {
37278                 height: 'auto !important',
37279                 'vertical-align': 'top'
37280             },
37281             '.x-grid-cal  .fc-event-hori' : {
37282                 height: '14px'
37283             }
37284              
37285             
37286         }, Roo.id());
37287     }
37288
37289     
37290     
37291 };
37292 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37293     /**
37294      * @cfg {Store} eventStore The store that loads events.
37295      */
37296     eventStore : 25,
37297
37298      
37299     activeDate : false,
37300     startDay : 0,
37301     autoWidth : true,
37302     monitorWindowResize : false,
37303
37304     
37305     resizeColumns : function() {
37306         var col = (this.view.el.getWidth() / 7) - 3;
37307         // loop through cols, and setWidth
37308         for(var i =0 ; i < 7 ; i++){
37309             this.cm.setColumnWidth(i, col);
37310         }
37311     },
37312      setDate :function(date) {
37313         
37314         Roo.log('setDate?');
37315         
37316         this.resizeColumns();
37317         var vd = this.activeDate;
37318         this.activeDate = date;
37319 //        if(vd && this.el){
37320 //            var t = date.getTime();
37321 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37322 //                Roo.log('using add remove');
37323 //                
37324 //                this.fireEvent('monthchange', this, date);
37325 //                
37326 //                this.cells.removeClass("fc-state-highlight");
37327 //                this.cells.each(function(c){
37328 //                   if(c.dateValue == t){
37329 //                       c.addClass("fc-state-highlight");
37330 //                       setTimeout(function(){
37331 //                            try{c.dom.firstChild.focus();}catch(e){}
37332 //                       }, 50);
37333 //                       return false;
37334 //                   }
37335 //                   return true;
37336 //                });
37337 //                return;
37338 //            }
37339 //        }
37340         
37341         var days = date.getDaysInMonth();
37342         
37343         var firstOfMonth = date.getFirstDateOfMonth();
37344         var startingPos = firstOfMonth.getDay()-this.startDay;
37345         
37346         if(startingPos < this.startDay){
37347             startingPos += 7;
37348         }
37349         
37350         var pm = date.add(Date.MONTH, -1);
37351         var prevStart = pm.getDaysInMonth()-startingPos;
37352 //        
37353         
37354         
37355         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37356         
37357         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37358         //this.cells.addClassOnOver('fc-state-hover');
37359         
37360         var cells = this.cells.elements;
37361         var textEls = this.textNodes;
37362         
37363         //Roo.each(cells, function(cell){
37364         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37365         //});
37366         
37367         days += startingPos;
37368
37369         // convert everything to numbers so it's fast
37370         var day = 86400000;
37371         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37372         //Roo.log(d);
37373         //Roo.log(pm);
37374         //Roo.log(prevStart);
37375         
37376         var today = new Date().clearTime().getTime();
37377         var sel = date.clearTime().getTime();
37378         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37379         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37380         var ddMatch = this.disabledDatesRE;
37381         var ddText = this.disabledDatesText;
37382         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37383         var ddaysText = this.disabledDaysText;
37384         var format = this.format;
37385         
37386         var setCellClass = function(cal, cell){
37387             
37388             //Roo.log('set Cell Class');
37389             cell.title = "";
37390             var t = d.getTime();
37391             
37392             //Roo.log(d);
37393             
37394             
37395             cell.dateValue = t;
37396             if(t == today){
37397                 cell.className += " fc-today";
37398                 cell.className += " fc-state-highlight";
37399                 cell.title = cal.todayText;
37400             }
37401             if(t == sel){
37402                 // disable highlight in other month..
37403                 cell.className += " fc-state-highlight";
37404                 
37405             }
37406             // disabling
37407             if(t < min) {
37408                 //cell.className = " fc-state-disabled";
37409                 cell.title = cal.minText;
37410                 return;
37411             }
37412             if(t > max) {
37413                 //cell.className = " fc-state-disabled";
37414                 cell.title = cal.maxText;
37415                 return;
37416             }
37417             if(ddays){
37418                 if(ddays.indexOf(d.getDay()) != -1){
37419                     // cell.title = ddaysText;
37420                    // cell.className = " fc-state-disabled";
37421                 }
37422             }
37423             if(ddMatch && format){
37424                 var fvalue = d.dateFormat(format);
37425                 if(ddMatch.test(fvalue)){
37426                     cell.title = ddText.replace("%0", fvalue);
37427                    cell.className = " fc-state-disabled";
37428                 }
37429             }
37430             
37431             if (!cell.initialClassName) {
37432                 cell.initialClassName = cell.dom.className;
37433             }
37434             
37435             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37436         };
37437
37438         var i = 0;
37439         
37440         for(; i < startingPos; i++) {
37441             cells[i].dayName =  (++prevStart);
37442             Roo.log(textEls[i]);
37443             d.setDate(d.getDate()+1);
37444             
37445             //cells[i].className = "fc-past fc-other-month";
37446             setCellClass(this, cells[i]);
37447         }
37448         
37449         var intDay = 0;
37450         
37451         for(; i < days; i++){
37452             intDay = i - startingPos + 1;
37453             cells[i].dayName =  (intDay);
37454             d.setDate(d.getDate()+1);
37455             
37456             cells[i].className = ''; // "x-date-active";
37457             setCellClass(this, cells[i]);
37458         }
37459         var extraDays = 0;
37460         
37461         for(; i < 42; i++) {
37462             //textEls[i].innerHTML = (++extraDays);
37463             
37464             d.setDate(d.getDate()+1);
37465             cells[i].dayName = (++extraDays);
37466             cells[i].className = "fc-future fc-other-month";
37467             setCellClass(this, cells[i]);
37468         }
37469         
37470         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37471         
37472         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37473         
37474         // this will cause all the cells to mis
37475         var rows= [];
37476         var i =0;
37477         for (var r = 0;r < 6;r++) {
37478             for (var c =0;c < 7;c++) {
37479                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37480             }    
37481         }
37482         
37483         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37484         for(i=0;i<cells.length;i++) {
37485             
37486             this.cells.elements[i].dayName = cells[i].dayName ;
37487             this.cells.elements[i].className = cells[i].className;
37488             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37489             this.cells.elements[i].title = cells[i].title ;
37490             this.cells.elements[i].dateValue = cells[i].dateValue ;
37491         }
37492         
37493         
37494         
37495         
37496         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37497         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37498         
37499         ////if(totalRows != 6){
37500             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37501            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37502        // }
37503         
37504         this.fireEvent('monthchange', this, date);
37505         
37506         
37507     },
37508  /**
37509      * Returns the grid's SelectionModel.
37510      * @return {SelectionModel}
37511      */
37512     getSelectionModel : function(){
37513         if(!this.selModel){
37514             this.selModel = new Roo.grid.CellSelectionModel();
37515         }
37516         return this.selModel;
37517     },
37518
37519     load: function() {
37520         this.eventStore.load()
37521         
37522         
37523         
37524     },
37525     
37526     findCell : function(dt) {
37527         dt = dt.clearTime().getTime();
37528         var ret = false;
37529         this.cells.each(function(c){
37530             //Roo.log("check " +c.dateValue + '?=' + dt);
37531             if(c.dateValue == dt){
37532                 ret = c;
37533                 return false;
37534             }
37535             return true;
37536         });
37537         
37538         return ret;
37539     },
37540     
37541     findCells : function(rec) {
37542         var s = rec.data.start_dt.clone().clearTime().getTime();
37543        // Roo.log(s);
37544         var e= rec.data.end_dt.clone().clearTime().getTime();
37545        // Roo.log(e);
37546         var ret = [];
37547         this.cells.each(function(c){
37548              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37549             
37550             if(c.dateValue > e){
37551                 return ;
37552             }
37553             if(c.dateValue < s){
37554                 return ;
37555             }
37556             ret.push(c);
37557         });
37558         
37559         return ret;    
37560     },
37561     
37562     findBestRow: function(cells)
37563     {
37564         var ret = 0;
37565         
37566         for (var i =0 ; i < cells.length;i++) {
37567             ret  = Math.max(cells[i].rows || 0,ret);
37568         }
37569         return ret;
37570         
37571     },
37572     
37573     
37574     addItem : function(rec)
37575     {
37576         // look for vertical location slot in
37577         var cells = this.findCells(rec);
37578         
37579         rec.row = this.findBestRow(cells);
37580         
37581         // work out the location.
37582         
37583         var crow = false;
37584         var rows = [];
37585         for(var i =0; i < cells.length; i++) {
37586             if (!crow) {
37587                 crow = {
37588                     start : cells[i],
37589                     end :  cells[i]
37590                 };
37591                 continue;
37592             }
37593             if (crow.start.getY() == cells[i].getY()) {
37594                 // on same row.
37595                 crow.end = cells[i];
37596                 continue;
37597             }
37598             // different row.
37599             rows.push(crow);
37600             crow = {
37601                 start: cells[i],
37602                 end : cells[i]
37603             };
37604             
37605         }
37606         
37607         rows.push(crow);
37608         rec.els = [];
37609         rec.rows = rows;
37610         rec.cells = cells;
37611         for (var i = 0; i < cells.length;i++) {
37612             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37613             
37614         }
37615         
37616         
37617     },
37618     
37619     clearEvents: function() {
37620         
37621         if (!this.eventStore.getCount()) {
37622             return;
37623         }
37624         // reset number of rows in cells.
37625         Roo.each(this.cells.elements, function(c){
37626             c.rows = 0;
37627         });
37628         
37629         this.eventStore.each(function(e) {
37630             this.clearEvent(e);
37631         },this);
37632         
37633     },
37634     
37635     clearEvent : function(ev)
37636     {
37637         if (ev.els) {
37638             Roo.each(ev.els, function(el) {
37639                 el.un('mouseenter' ,this.onEventEnter, this);
37640                 el.un('mouseleave' ,this.onEventLeave, this);
37641                 el.remove();
37642             },this);
37643             ev.els = [];
37644         }
37645     },
37646     
37647     
37648     renderEvent : function(ev,ctr) {
37649         if (!ctr) {
37650              ctr = this.view.el.select('.fc-event-container',true).first();
37651         }
37652         
37653          
37654         this.clearEvent(ev);
37655             //code
37656        
37657         
37658         
37659         ev.els = [];
37660         var cells = ev.cells;
37661         var rows = ev.rows;
37662         this.fireEvent('eventrender', this, ev);
37663         
37664         for(var i =0; i < rows.length; i++) {
37665             
37666             cls = '';
37667             if (i == 0) {
37668                 cls += ' fc-event-start';
37669             }
37670             if ((i+1) == rows.length) {
37671                 cls += ' fc-event-end';
37672             }
37673             
37674             //Roo.log(ev.data);
37675             // how many rows should it span..
37676             var cg = this.eventTmpl.append(ctr,Roo.apply({
37677                 fccls : cls
37678                 
37679             }, ev.data) , true);
37680             
37681             
37682             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37683             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37684             cg.on('click', this.onEventClick, this, ev);
37685             
37686             ev.els.push(cg);
37687             
37688             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37689             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37690             //Roo.log(cg);
37691              
37692             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
37693             cg.setWidth(ebox.right - sbox.x -2);
37694         }
37695     },
37696     
37697     renderEvents: function()
37698     {   
37699         // first make sure there is enough space..
37700         
37701         if (!this.eventTmpl) {
37702             this.eventTmpl = new Roo.Template(
37703                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
37704                     '<div class="fc-event-inner">' +
37705                         '<span class="fc-event-time">{time}</span>' +
37706                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37707                     '</div>' +
37708                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
37709                 '</div>'
37710             );
37711                 
37712         }
37713                
37714         
37715         
37716         this.cells.each(function(c) {
37717             //Roo.log(c.select('.fc-day-content div',true).first());
37718             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37719         });
37720         
37721         var ctr = this.view.el.select('.fc-event-container',true).first();
37722         
37723         var cls;
37724         this.eventStore.each(function(ev){
37725             
37726             this.renderEvent(ev);
37727              
37728              
37729         }, this);
37730         this.view.layout();
37731         
37732     },
37733     
37734     onEventEnter: function (e, el,event,d) {
37735         this.fireEvent('evententer', this, el, event);
37736     },
37737     
37738     onEventLeave: function (e, el,event,d) {
37739         this.fireEvent('eventleave', this, el, event);
37740     },
37741     
37742     onEventClick: function (e, el,event,d) {
37743         this.fireEvent('eventclick', this, el, event);
37744     },
37745     
37746     onMonthChange: function () {
37747         this.store.load();
37748     },
37749     
37750     onLoad: function () {
37751         
37752         //Roo.log('calendar onload');
37753 //         
37754         if(this.eventStore.getCount() > 0){
37755             
37756            
37757             
37758             this.eventStore.each(function(d){
37759                 
37760                 
37761                 // FIXME..
37762                 var add =   d.data;
37763                 if (typeof(add.end_dt) == 'undefined')  {
37764                     Roo.log("Missing End time in calendar data: ");
37765                     Roo.log(d);
37766                     return;
37767                 }
37768                 if (typeof(add.start_dt) == 'undefined')  {
37769                     Roo.log("Missing Start time in calendar data: ");
37770                     Roo.log(d);
37771                     return;
37772                 }
37773                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
37774                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
37775                 add.id = add.id || d.id;
37776                 add.title = add.title || '??';
37777                 
37778                 this.addItem(d);
37779                 
37780              
37781             },this);
37782         }
37783         
37784         this.renderEvents();
37785     }
37786     
37787
37788 });
37789 /*
37790  grid : {
37791                 xtype: 'Grid',
37792                 xns: Roo.grid,
37793                 listeners : {
37794                     render : function ()
37795                     {
37796                         _this.grid = this;
37797                         
37798                         if (!this.view.el.hasClass('course-timesheet')) {
37799                             this.view.el.addClass('course-timesheet');
37800                         }
37801                         if (this.tsStyle) {
37802                             this.ds.load({});
37803                             return; 
37804                         }
37805                         Roo.log('width');
37806                         Roo.log(_this.grid.view.el.getWidth());
37807                         
37808                         
37809                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
37810                             '.course-timesheet .x-grid-row' : {
37811                                 height: '80px'
37812                             },
37813                             '.x-grid-row td' : {
37814                                 'vertical-align' : 0
37815                             },
37816                             '.course-edit-link' : {
37817                                 'color' : 'blue',
37818                                 'text-overflow' : 'ellipsis',
37819                                 'overflow' : 'hidden',
37820                                 'white-space' : 'nowrap',
37821                                 'cursor' : 'pointer'
37822                             },
37823                             '.sub-link' : {
37824                                 'color' : 'green'
37825                             },
37826                             '.de-act-sup-link' : {
37827                                 'color' : 'purple',
37828                                 'text-decoration' : 'line-through'
37829                             },
37830                             '.de-act-link' : {
37831                                 'color' : 'red',
37832                                 'text-decoration' : 'line-through'
37833                             },
37834                             '.course-timesheet .course-highlight' : {
37835                                 'border-top-style': 'dashed !important',
37836                                 'border-bottom-bottom': 'dashed !important'
37837                             },
37838                             '.course-timesheet .course-item' : {
37839                                 'font-family'   : 'tahoma, arial, helvetica',
37840                                 'font-size'     : '11px',
37841                                 'overflow'      : 'hidden',
37842                                 'padding-left'  : '10px',
37843                                 'padding-right' : '10px',
37844                                 'padding-top' : '10px' 
37845                             }
37846                             
37847                         }, Roo.id());
37848                                 this.ds.load({});
37849                     }
37850                 },
37851                 autoWidth : true,
37852                 monitorWindowResize : false,
37853                 cellrenderer : function(v,x,r)
37854                 {
37855                     return v;
37856                 },
37857                 sm : {
37858                     xtype: 'CellSelectionModel',
37859                     xns: Roo.grid
37860                 },
37861                 dataSource : {
37862                     xtype: 'Store',
37863                     xns: Roo.data,
37864                     listeners : {
37865                         beforeload : function (_self, options)
37866                         {
37867                             options.params = options.params || {};
37868                             options.params._month = _this.monthField.getValue();
37869                             options.params.limit = 9999;
37870                             options.params['sort'] = 'when_dt';    
37871                             options.params['dir'] = 'ASC';    
37872                             this.proxy.loadResponse = this.loadResponse;
37873                             Roo.log("load?");
37874                             //this.addColumns();
37875                         },
37876                         load : function (_self, records, options)
37877                         {
37878                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
37879                                 // if you click on the translation.. you can edit it...
37880                                 var el = Roo.get(this);
37881                                 var id = el.dom.getAttribute('data-id');
37882                                 var d = el.dom.getAttribute('data-date');
37883                                 var t = el.dom.getAttribute('data-time');
37884                                 //var id = this.child('span').dom.textContent;
37885                                 
37886                                 //Roo.log(this);
37887                                 Pman.Dialog.CourseCalendar.show({
37888                                     id : id,
37889                                     when_d : d,
37890                                     when_t : t,
37891                                     productitem_active : id ? 1 : 0
37892                                 }, function() {
37893                                     _this.grid.ds.load({});
37894                                 });
37895                            
37896                            });
37897                            
37898                            _this.panel.fireEvent('resize', [ '', '' ]);
37899                         }
37900                     },
37901                     loadResponse : function(o, success, response){
37902                             // this is overridden on before load..
37903                             
37904                             Roo.log("our code?");       
37905                             //Roo.log(success);
37906                             //Roo.log(response)
37907                             delete this.activeRequest;
37908                             if(!success){
37909                                 this.fireEvent("loadexception", this, o, response);
37910                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37911                                 return;
37912                             }
37913                             var result;
37914                             try {
37915                                 result = o.reader.read(response);
37916                             }catch(e){
37917                                 Roo.log("load exception?");
37918                                 this.fireEvent("loadexception", this, o, response, e);
37919                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37920                                 return;
37921                             }
37922                             Roo.log("ready...");        
37923                             // loop through result.records;
37924                             // and set this.tdate[date] = [] << array of records..
37925                             _this.tdata  = {};
37926                             Roo.each(result.records, function(r){
37927                                 //Roo.log(r.data);
37928                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
37929                                     _this.tdata[r.data.when_dt.format('j')] = [];
37930                                 }
37931                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
37932                             });
37933                             
37934                             //Roo.log(_this.tdata);
37935                             
37936                             result.records = [];
37937                             result.totalRecords = 6;
37938                     
37939                             // let's generate some duumy records for the rows.
37940                             //var st = _this.dateField.getValue();
37941                             
37942                             // work out monday..
37943                             //st = st.add(Date.DAY, -1 * st.format('w'));
37944                             
37945                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37946                             
37947                             var firstOfMonth = date.getFirstDayOfMonth();
37948                             var days = date.getDaysInMonth();
37949                             var d = 1;
37950                             var firstAdded = false;
37951                             for (var i = 0; i < result.totalRecords ; i++) {
37952                                 //var d= st.add(Date.DAY, i);
37953                                 var row = {};
37954                                 var added = 0;
37955                                 for(var w = 0 ; w < 7 ; w++){
37956                                     if(!firstAdded && firstOfMonth != w){
37957                                         continue;
37958                                     }
37959                                     if(d > days){
37960                                         continue;
37961                                     }
37962                                     firstAdded = true;
37963                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
37964                                     row['weekday'+w] = String.format(
37965                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
37966                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
37967                                                     d,
37968                                                     date.format('Y-m-')+dd
37969                                                 );
37970                                     added++;
37971                                     if(typeof(_this.tdata[d]) != 'undefined'){
37972                                         Roo.each(_this.tdata[d], function(r){
37973                                             var is_sub = '';
37974                                             var deactive = '';
37975                                             var id = r.id;
37976                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
37977                                             if(r.parent_id*1>0){
37978                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
37979                                                 id = r.parent_id;
37980                                             }
37981                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
37982                                                 deactive = 'de-act-link';
37983                                             }
37984                                             
37985                                             row['weekday'+w] += String.format(
37986                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
37987                                                     id, //0
37988                                                     r.product_id_name, //1
37989                                                     r.when_dt.format('h:ia'), //2
37990                                                     is_sub, //3
37991                                                     deactive, //4
37992                                                     desc // 5
37993                                             );
37994                                         });
37995                                     }
37996                                     d++;
37997                                 }
37998                                 
37999                                 // only do this if something added..
38000                                 if(added > 0){ 
38001                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38002                                 }
38003                                 
38004                                 
38005                                 // push it twice. (second one with an hour..
38006                                 
38007                             }
38008                             //Roo.log(result);
38009                             this.fireEvent("load", this, o, o.request.arg);
38010                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38011                         },
38012                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38013                     proxy : {
38014                         xtype: 'HttpProxy',
38015                         xns: Roo.data,
38016                         method : 'GET',
38017                         url : baseURL + '/Roo/Shop_course.php'
38018                     },
38019                     reader : {
38020                         xtype: 'JsonReader',
38021                         xns: Roo.data,
38022                         id : 'id',
38023                         fields : [
38024                             {
38025                                 'name': 'id',
38026                                 'type': 'int'
38027                             },
38028                             {
38029                                 'name': 'when_dt',
38030                                 'type': 'string'
38031                             },
38032                             {
38033                                 'name': 'end_dt',
38034                                 'type': 'string'
38035                             },
38036                             {
38037                                 'name': 'parent_id',
38038                                 'type': 'int'
38039                             },
38040                             {
38041                                 'name': 'product_id',
38042                                 'type': 'int'
38043                             },
38044                             {
38045                                 'name': 'productitem_id',
38046                                 'type': 'int'
38047                             },
38048                             {
38049                                 'name': 'guid',
38050                                 'type': 'int'
38051                             }
38052                         ]
38053                     }
38054                 },
38055                 toolbar : {
38056                     xtype: 'Toolbar',
38057                     xns: Roo,
38058                     items : [
38059                         {
38060                             xtype: 'Button',
38061                             xns: Roo.Toolbar,
38062                             listeners : {
38063                                 click : function (_self, e)
38064                                 {
38065                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38066                                     sd.setMonth(sd.getMonth()-1);
38067                                     _this.monthField.setValue(sd.format('Y-m-d'));
38068                                     _this.grid.ds.load({});
38069                                 }
38070                             },
38071                             text : "Back"
38072                         },
38073                         {
38074                             xtype: 'Separator',
38075                             xns: Roo.Toolbar
38076                         },
38077                         {
38078                             xtype: 'MonthField',
38079                             xns: Roo.form,
38080                             listeners : {
38081                                 render : function (_self)
38082                                 {
38083                                     _this.monthField = _self;
38084                                    // _this.monthField.set  today
38085                                 },
38086                                 select : function (combo, date)
38087                                 {
38088                                     _this.grid.ds.load({});
38089                                 }
38090                             },
38091                             value : (function() { return new Date(); })()
38092                         },
38093                         {
38094                             xtype: 'Separator',
38095                             xns: Roo.Toolbar
38096                         },
38097                         {
38098                             xtype: 'TextItem',
38099                             xns: Roo.Toolbar,
38100                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38101                         },
38102                         {
38103                             xtype: 'Fill',
38104                             xns: Roo.Toolbar
38105                         },
38106                         {
38107                             xtype: 'Button',
38108                             xns: Roo.Toolbar,
38109                             listeners : {
38110                                 click : function (_self, e)
38111                                 {
38112                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38113                                     sd.setMonth(sd.getMonth()+1);
38114                                     _this.monthField.setValue(sd.format('Y-m-d'));
38115                                     _this.grid.ds.load({});
38116                                 }
38117                             },
38118                             text : "Next"
38119                         }
38120                     ]
38121                 },
38122                  
38123             }
38124         };
38125         
38126         *//*
38127  * Based on:
38128  * Ext JS Library 1.1.1
38129  * Copyright(c) 2006-2007, Ext JS, LLC.
38130  *
38131  * Originally Released Under LGPL - original licence link has changed is not relivant.
38132  *
38133  * Fork - LGPL
38134  * <script type="text/javascript">
38135  */
38136  
38137 /**
38138  * @class Roo.LoadMask
38139  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38140  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38141  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38142  * element's UpdateManager load indicator and will be destroyed after the initial load.
38143  * @constructor
38144  * Create a new LoadMask
38145  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38146  * @param {Object} config The config object
38147  */
38148 Roo.LoadMask = function(el, config){
38149     this.el = Roo.get(el);
38150     Roo.apply(this, config);
38151     if(this.store){
38152         this.store.on('beforeload', this.onBeforeLoad, this);
38153         this.store.on('load', this.onLoad, this);
38154         this.store.on('loadexception', this.onLoadException, this);
38155         this.removeMask = false;
38156     }else{
38157         var um = this.el.getUpdateManager();
38158         um.showLoadIndicator = false; // disable the default indicator
38159         um.on('beforeupdate', this.onBeforeLoad, this);
38160         um.on('update', this.onLoad, this);
38161         um.on('failure', this.onLoad, this);
38162         this.removeMask = true;
38163     }
38164 };
38165
38166 Roo.LoadMask.prototype = {
38167     /**
38168      * @cfg {Boolean} removeMask
38169      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38170      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38171      */
38172     /**
38173      * @cfg {String} msg
38174      * The text to display in a centered loading message box (defaults to 'Loading...')
38175      */
38176     msg : 'Loading...',
38177     /**
38178      * @cfg {String} msgCls
38179      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38180      */
38181     msgCls : 'x-mask-loading',
38182
38183     /**
38184      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38185      * @type Boolean
38186      */
38187     disabled: false,
38188
38189     /**
38190      * Disables the mask to prevent it from being displayed
38191      */
38192     disable : function(){
38193        this.disabled = true;
38194     },
38195
38196     /**
38197      * Enables the mask so that it can be displayed
38198      */
38199     enable : function(){
38200         this.disabled = false;
38201     },
38202     
38203     onLoadException : function()
38204     {
38205         Roo.log(arguments);
38206         
38207         if (typeof(arguments[3]) != 'undefined') {
38208             Roo.MessageBox.alert("Error loading",arguments[3]);
38209         } 
38210         /*
38211         try {
38212             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38213                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38214             }   
38215         } catch(e) {
38216             
38217         }
38218         */
38219     
38220         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38221     },
38222     // private
38223     onLoad : function()
38224     {
38225         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38226     },
38227
38228     // private
38229     onBeforeLoad : function(){
38230         if(!this.disabled){
38231             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38232         }
38233     },
38234
38235     // private
38236     destroy : function(){
38237         if(this.store){
38238             this.store.un('beforeload', this.onBeforeLoad, this);
38239             this.store.un('load', this.onLoad, this);
38240             this.store.un('loadexception', this.onLoadException, this);
38241         }else{
38242             var um = this.el.getUpdateManager();
38243             um.un('beforeupdate', this.onBeforeLoad, this);
38244             um.un('update', this.onLoad, this);
38245             um.un('failure', this.onLoad, this);
38246         }
38247     }
38248 };/*
38249  * Based on:
38250  * Ext JS Library 1.1.1
38251  * Copyright(c) 2006-2007, Ext JS, LLC.
38252  *
38253  * Originally Released Under LGPL - original licence link has changed is not relivant.
38254  *
38255  * Fork - LGPL
38256  * <script type="text/javascript">
38257  */
38258
38259
38260 /**
38261  * @class Roo.XTemplate
38262  * @extends Roo.Template
38263  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38264 <pre><code>
38265 var t = new Roo.XTemplate(
38266         '&lt;select name="{name}"&gt;',
38267                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38268         '&lt;/select&gt;'
38269 );
38270  
38271 // then append, applying the master template values
38272  </code></pre>
38273  *
38274  * Supported features:
38275  *
38276  *  Tags:
38277
38278 <pre><code>
38279       {a_variable} - output encoded.
38280       {a_variable.format:("Y-m-d")} - call a method on the variable
38281       {a_variable:raw} - unencoded output
38282       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38283       {a_variable:this.method_on_template(...)} - call a method on the template object.
38284  
38285 </code></pre>
38286  *  The tpl tag:
38287 <pre><code>
38288         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38289         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38290         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38291         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38292   
38293         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38294         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38295 </code></pre>
38296  *      
38297  */
38298 Roo.XTemplate = function()
38299 {
38300     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38301     if (this.html) {
38302         this.compile();
38303     }
38304 };
38305
38306
38307 Roo.extend(Roo.XTemplate, Roo.Template, {
38308
38309     /**
38310      * The various sub templates
38311      */
38312     tpls : false,
38313     /**
38314      *
38315      * basic tag replacing syntax
38316      * WORD:WORD()
38317      *
38318      * // you can fake an object call by doing this
38319      *  x.t:(test,tesT) 
38320      * 
38321      */
38322     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38323
38324     /**
38325      * compile the template
38326      *
38327      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38328      *
38329      */
38330     compile: function()
38331     {
38332         var s = this.html;
38333      
38334         s = ['<tpl>', s, '</tpl>'].join('');
38335     
38336         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38337             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38338             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38339             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38340             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38341             m,
38342             id     = 0,
38343             tpls   = [];
38344     
38345         while(true == !!(m = s.match(re))){
38346             var forMatch   = m[0].match(nameRe),
38347                 ifMatch   = m[0].match(ifRe),
38348                 execMatch   = m[0].match(execRe),
38349                 namedMatch   = m[0].match(namedRe),
38350                 
38351                 exp  = null, 
38352                 fn   = null,
38353                 exec = null,
38354                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38355                 
38356             if (ifMatch) {
38357                 // if - puts fn into test..
38358                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38359                 if(exp){
38360                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38361                 }
38362             }
38363             
38364             if (execMatch) {
38365                 // exec - calls a function... returns empty if true is  returned.
38366                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38367                 if(exp){
38368                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38369                 }
38370             }
38371             
38372             
38373             if (name) {
38374                 // for = 
38375                 switch(name){
38376                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38377                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38378                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38379                 }
38380             }
38381             var uid = namedMatch ? namedMatch[1] : id;
38382             
38383             
38384             tpls.push({
38385                 id:     namedMatch ? namedMatch[1] : id,
38386                 target: name,
38387                 exec:   exec,
38388                 test:   fn,
38389                 body:   m[1] || ''
38390             });
38391             if (namedMatch) {
38392                 s = s.replace(m[0], '');
38393             } else { 
38394                 s = s.replace(m[0], '{xtpl'+ id + '}');
38395             }
38396             ++id;
38397         }
38398         this.tpls = [];
38399         for(var i = tpls.length-1; i >= 0; --i){
38400             this.compileTpl(tpls[i]);
38401             this.tpls[tpls[i].id] = tpls[i];
38402         }
38403         this.master = tpls[tpls.length-1];
38404         return this;
38405     },
38406     /**
38407      * same as applyTemplate, except it's done to one of the subTemplates
38408      * when using named templates, you can do:
38409      *
38410      * var str = pl.applySubTemplate('your-name', values);
38411      *
38412      * 
38413      * @param {Number} id of the template
38414      * @param {Object} values to apply to template
38415      * @param {Object} parent (normaly the instance of this object)
38416      */
38417     applySubTemplate : function(id, values, parent)
38418     {
38419         
38420         
38421         var t = this.tpls[id];
38422         
38423         
38424         try { 
38425             if(t.test && !t.test.call(this, values, parent)){
38426                 return '';
38427             }
38428         } catch(e) {
38429             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38430             Roo.log(e.toString());
38431             Roo.log(t.test);
38432             return ''
38433         }
38434         try { 
38435             
38436             if(t.exec && t.exec.call(this, values, parent)){
38437                 return '';
38438             }
38439         } catch(e) {
38440             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38441             Roo.log(e.toString());
38442             Roo.log(t.exec);
38443             return ''
38444         }
38445         try {
38446             var vs = t.target ? t.target.call(this, values, parent) : values;
38447             parent = t.target ? values : parent;
38448             if(t.target && vs instanceof Array){
38449                 var buf = [];
38450                 for(var i = 0, len = vs.length; i < len; i++){
38451                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38452                 }
38453                 return buf.join('');
38454             }
38455             return t.compiled.call(this, vs, parent);
38456         } catch (e) {
38457             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38458             Roo.log(e.toString());
38459             Roo.log(t.compiled);
38460             return '';
38461         }
38462     },
38463
38464     compileTpl : function(tpl)
38465     {
38466         var fm = Roo.util.Format;
38467         var useF = this.disableFormats !== true;
38468         var sep = Roo.isGecko ? "+" : ",";
38469         var undef = function(str) {
38470             Roo.log("Property not found :"  + str);
38471             return '';
38472         };
38473         
38474         var fn = function(m, name, format, args)
38475         {
38476             //Roo.log(arguments);
38477             args = args ? args.replace(/\\'/g,"'") : args;
38478             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38479             if (typeof(format) == 'undefined') {
38480                 format= 'htmlEncode';
38481             }
38482             if (format == 'raw' ) {
38483                 format = false;
38484             }
38485             
38486             if(name.substr(0, 4) == 'xtpl'){
38487                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38488             }
38489             
38490             // build an array of options to determine if value is undefined..
38491             
38492             // basically get 'xxxx.yyyy' then do
38493             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38494             //    (function () { Roo.log("Property not found"); return ''; })() :
38495             //    ......
38496             
38497             var udef_ar = [];
38498             var lookfor = '';
38499             Roo.each(name.split('.'), function(st) {
38500                 lookfor += (lookfor.length ? '.': '') + st;
38501                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38502             });
38503             
38504             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38505             
38506             
38507             if(format && useF){
38508                 
38509                 args = args ? ',' + args : "";
38510                  
38511                 if(format.substr(0, 5) != "this."){
38512                     format = "fm." + format + '(';
38513                 }else{
38514                     format = 'this.call("'+ format.substr(5) + '", ';
38515                     args = ", values";
38516                 }
38517                 
38518                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38519             }
38520              
38521             if (args.length) {
38522                 // called with xxyx.yuu:(test,test)
38523                 // change to ()
38524                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38525             }
38526             // raw.. - :raw modifier..
38527             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38528             
38529         };
38530         var body;
38531         // branched to use + in gecko and [].join() in others
38532         if(Roo.isGecko){
38533             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38534                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38535                     "';};};";
38536         }else{
38537             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38538             body.push(tpl.body.replace(/(\r\n|\n)/g,
38539                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38540             body.push("'].join('');};};");
38541             body = body.join('');
38542         }
38543         
38544         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38545        
38546         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38547         eval(body);
38548         
38549         return this;
38550     },
38551
38552     applyTemplate : function(values){
38553         return this.master.compiled.call(this, values, {});
38554         //var s = this.subs;
38555     },
38556
38557     apply : function(){
38558         return this.applyTemplate.apply(this, arguments);
38559     }
38560
38561  });
38562
38563 Roo.XTemplate.from = function(el){
38564     el = Roo.getDom(el);
38565     return new Roo.XTemplate(el.value || el.innerHTML);
38566 };